【问题标题】:Parsing CSS background-image解析 CSS 背景图像
【发布时间】:2011-10-21 15:55:35
【问题描述】:

如何解析 CSS background-image,它支持多个值,可能是 none 和具有多个逗号分隔参数的函数(例如 url()linear-gradient())?我似乎无法用正则表达式正确地做到这一点。一个好的测试用例如下:

  linear-gradient(top left, red, rgba(255,0,0,0))
, url(a)
, image(url(b.svg), 'b.png' 150dpi, 'b.gif', rgba(0,0,255,0.5))
, none

我想将其转换为以下数组:

[
      "linear-gradient(top left, red, rgba(255,0,0,0))"
    , "url(a)"
    , "image(url(b.svg), 'b.png' 150dpi, 'b.gif', rgba(0,0,255,0.5))"
    , "none"
]

【问题讨论】:

  • 这只是一个例子。可能是 40 url()s,然后是一些渐变。
  • 是否有浏览器支持image(...linear-gradient(... for background-image?很确定 FF 和 Chrome 不会。
  • 是的,他们支持linear-gradient()。至于image(),他们支持解析它,这样如果有多个背景图像,他们可以跳过它。
  • really?也许你有一个例子? Here's one of mine.
  • 这是徒劳的,与我的问题完全无关。他们当然支持它,它只是供应商前缀。

标签: javascript css regex


【解决方案1】:
function split (string) {
    var token = /((?:[^"']|".*?"|'.*?')*?)([(,)]|$)/g;
    return (function recurse () {
        for (var array = [];;) {
            var result = token.exec(string);
            if (result[2] == '(') {
                array.push(result[1].trim() + '(' + recurse().join(',') + ')');
                result = token.exec(string);
            } else array.push(result[1].trim());
            if (result[2] != ',') return array
        }
    })()
}

split("linear-gradient(top left, red, rgba(255,0,0,0)), url(a), image(url" +
      "(b.svg), 'b.png' 150dpi, 'b.gif', rgba(0,0,255,0.5)), none").toSource()

["linear-gradient(top left,red,rgba(255,0,0,0))", "url(a)",
 "image(url(b.svg),'b.png' 150dpi,'b.gif',rgba(0,0,255,0.5))", "none"]

【讨论】:

  • @brock-adams 没错,对于这样的浏览器,它需要这样一行: if (!String.prototype.trim) String.prototype.trim = function () { return this.replace (/^\s+|\s+$/g, '') }
  • 我发现这也会从渐变颜色停止中去除百分比值,因此:- linear-gradient(67deg,rgb(30, 87, 153) 0%,rgb(41, 137, 216) 50%,rgb(32, 124, 202) 51%,rgb(125, 185, 232) 100%) 变成这个线性梯度(67deg,rgb(30,87,153),rgb(41,137,216),rgb(32,124,202), rgb(125,185,232)) 真的很想知道如何调整正则表达式,这样就不会发生(无论我多么努力,正则表达式都让我感到困惑)
  • 我想我解决了@DavidO'Sullivan,在if (result[2] != ',') return array 之后添加else if (result[2] == ',' && result[1][result[1].length - 1] == '%') array[array.length - 1] += result[1];,它应该会捕获百分比。
【解决方案2】:

查看当前针对 CSS3 的 W3C 候选推荐(具体参见 background-imageuri),其结构如下:

<background-image> = <bg-image> [ , <bg-image> ]* 
<bg-image> = <image> | none
<image> = <url> | <image-list> | <element-reference> | <image-combination> | <gradient>

...(您可以找到图片的其余语法here

编辑:

您需要解析匹配的括号或none,而前者不能使用正则表达式。这篇文章有一个算法的伪代码:Python parsing bracketed blocks

【讨论】:

  • 我没有自己解析值,只是将它们分开,所以 URI 部分并不是真正需要的。
  • 谢谢,虽然我会考虑将逗号分隔的列表支持集成到该算法中,但我不确定我应该如何去做。
【解决方案3】:

我知道,这个问题已经很老了,但如果你偶然发现它并需要其他解决方案,你可以使用这个:

let mydiv = document.getElementById('mydiv'),
  result = document.getElementById('result'),
  bgImageArray = getBackgroundImageArray(mydiv);

console.log(bgImageArray);

result.innerHTML = bgImageArray.join('<hr>');

function getBackgroundImageArray(el)
{
    // get backgroundImageStyle
    let bgimg_style = getComputedStyle(el).backgroundImage,
        // count for parenthesis
        parenthesis = -1;

    // split background by characters...
    return bgimg_style.split('').reduce((str_to_split, character) => {
        // if opening parenthesis
        if(character === '(') {
            // if first opening parenthesis set parenthesis count to zero
            if(parenthesis === -1) {
                parenthesis = 0;
            }
            // add 1 to parenthesis count
            parenthesis++;
        }
        // for closing parenthesis reduce parenthesis count by 1
        else if(character === ')') {
            parenthesis--;
        }
        else {
            // if current character is a comma and it is not inside a parenthesis, at a "split" character
            if(character === ',') {
                if(parenthesis === 0) {
                    str_to_split += '||';
                    return str_to_split;
                }
            }
        }
    
        // else keep the character
        str_to_split += character;
    
        return str_to_split;
    }, '')
    // split the resulting string by the split characters including whitespaces before and after to generate an array
    .split(/\s*\|\|\s*/);
}
#mydiv {
  height: 75px;
  background-image:
    /* first bg image */
    linear-gradient(90deg, rgba(28,221,218,1) 0%, rgba(45,109,210,1) 35%, rgba(0,212,255,1) 100%),
    
    /* second bg image */
    -webkit-image-set(url(nothing.jpg) 1x, url(everything.png) 2x),

    /* third bg image */
    url('there/is/an/image.svg');
    
}
<div id="mydiv"></div>

<p>See in console for actual result array, below is the array splitted by &lt;hr&gt;</p>

<div id="result"></div>

【讨论】:

  • 哇,感谢十年后这个深思熟虑的答案!