【问题标题】:How to extract class names from a CSS selector?如何从 CSS 选择器中提取类名?
【发布时间】:2021-08-13 06:57:01
【问题描述】:

故事:

我目前正在构建一个 ESLint 规则,以警告在 CSS 选择器定位器中使用引导布局导向和角度技术类。目前我在字符串方法中使用一个简单的子字符串:

for (var i = 0; i < prohibitedClasses.length; i++) {
  if (node.arguments[0].value.indexOf(prohibitedClasses[i]) >= 0) {
    context.report({
      node: node,
      message: 'Unexpected Bootstrap class "' + prohibitedClasses[i] + '" inside a CSS selector'
    })
  }

但它并没有被证明是可靠的。例如,它在 .col-sm-offset-11 CSS 选择器上抛出 2 次错误,报告同时使用 col-sm-offset-1col-sm-offset-11。我可以想象它可以很容易地在使用多个伪类的更复杂的选择器上中断。


问题:

从 CSS 选择器中提取类名最可靠的方法是什么?


这是我们应该涵盖的示例测试列表(有待改进):

.col-sm-push-4                 // -> ['col-sm-push-4']
.myclass.col-lg-pull-8         // -> ['myclass', 'col-lg-pull-8']
[class*='col-md-offset-4']     // -> []
[class$=col-md-offset-11]      // -> []
[class~="col-md-10"] .myclass  // -> ['col-md-10', 'myclass']
.col-md-10,.col-md-11          // -> ['col-md-10', 'col-md-11']

请注意,我们需要跳过 ^=$=*= 部分类过滤器值,留下 ~=(感谢 cmets)。

【问题讨论】:

  • 我同意@adeneo。这是一个有趣的问题,但“使用这个库” 并不是一个很好的答案。它没有直接解决“如何”的问题。
  • @squint 好点。我对解决“如何”的答案完全开放。只是想分享有趣的问题以及我处理它的方式。谢谢!
  • 从 *= 和 $= 属性选择器中提取类名是不可靠的,除非您假设它们永远不会匹配类名恰好包含这些子字符串但不等于它们的元素。
  • @BoltClock 啊,我应该只考虑“严格相等”。让我看看我是否应该相应地更新问题。谢谢。
  • 请注意,~= 另一方面是可靠的——在 HTML 中,它相当于一个类选择器。

标签: javascript css parsing css-selectors eslint


【解决方案1】:

有一个专门为问题包设计的名为node-css-selector-parser,它缺少“如何使用它”部分来提取类名。填补空白,这是我将其应用于问题的方法。

使用node-css-selector-parser,我们可以解析 CSS 选择器并根据结果类型分析带有点的类名(例如.myclass)和属性选择器内使用的类名(例如[class*=test]):

// setup up CSS selector parser
var CssSelectorParser = require('css-selector-parser').CssSelectorParser
var parser = new CssSelectorParser()

parser.registerSelectorPseudos('has', 'contains')
parser.registerNestingOperators('>', '+', '~')
parser.registerAttrEqualityMods('^', '$', '*', '~')
parser.enableSubstitutes()

function extractClassNames (rule) {
  var classNames = []
  // extract class names defined with ".", e.g. .myclass
  if (rule.classNames) {
    classNames.push.apply(classNames, rule.classNames)
  }

  // extract class names defined in attributes, e.g. [class*=myclass]
  if (rule.attrs) {
    rule.attrs.forEach(function (attr) {
      if (attr.name === 'class') {
        classNames.push(attr.value)
      }
    })
  }

  return classNames
}

module.exports = function (cssSelector) {
  try {
    var result = parser.parse(cssSelector)
  } catch (err) {
    // ignore parsing errors - we don't want it to fail miserably on a target machine during a ESLint run
    console.log('Parsing CSS selector: "' + cssSelector + '". ' + err)
    return []
  }

  // handling empty inputs
  if (!result) {
    return []
  }

  var classNames = []

  if (result.type === 'ruleSet') {
    var rule = result.rule
    while (rule) {
      classNames.push.apply(classNames, extractClassNames(rule))
      rule = rule.rule
    }
  } else if (result.type === 'selectors' && result.selectors) {
    result.selectors.forEach(function (selector) {
      var srule = selector.rule
      while (srule) {
        classNames.push.apply(classNames, extractClassNames(srule))
        srule = srule.rule;
      }
    })
  }
  return classNames
}

(使用standard 代码样式 - 因此,例如,在行尾没有;

事实证明这对我有用,希望能帮助其他有类似问题的人。请注意,在这种状态下,此代码还将提取传递给 ^=$=*= 的部分类值,理想情况下需要跳过这些值。

【讨论】:

    猜你喜欢
    • 2017-11-16
    • 1970-01-01
    • 2023-02-15
    • 2014-03-21
    • 1970-01-01
    • 2018-05-09
    • 2010-12-30
    • 2021-10-23
    • 1970-01-01
    相关资源
    最近更新 更多