2016-07-17 9 views
2

Die Story:Wie Klassennamen aus einem CSS-Selektor extrahiert werden?

ich zur Zeit eine ESLint Regel bauen über die Verwendung von Bootstrap layoutorientierte und Winkel technische Klassen innerhalb CSS-Selektor-Locators zu warnen. Zur Zeit verwende ich eine einfache Teilzeichenkette in einer Zeichenkettenmethode:

Aber es hat sich nicht als zuverlässig erwiesen. Zum Beispiel gibt es einen Fehler 2 Mal auf .col-sm-offset-11 CSS-Selektor Berichterstattung sowohl und col-sm-offset-11 zu verwenden. Ich kann mir vorstellen, dass es bei komplexeren Selektoren mit mehreren verwendeten Pseudoklassen leicht brechen kann.


Die Frage:

Was ist die zuverlässigste Weise Klassennamen von einem CSS-Selektor zu extrahieren?


Hier ist eine Probe-Test Liste, die wir (zu verbessern) abdecken sollte:

.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'] 

Beachten Sie, dass wir die die ~= (dank verlassen ^=, $= und *= Teilklasse Filterwerte müssen überspringen für die Kommentare).

+2

Ich stimme mit @adeneo überein. Es ist eine interessante Frage, aber * "benutze diese Bibliothek" * ist keine große Antwort. Es geht nicht direkt um die Frage "wie". –

+0

@squint guter Punkt. Ich bin völlig offen für Antworten auf das "Wie". Ich wollte nur das interessante Problem und die Art, wie ich es angegangen bin, teilen. Vielen Dank! – alecxe

+0

Es ist nicht zuverlässig, Klassennamen aus den Attributen * = und $ = zu extrahieren, es sei denn, Sie nehmen an, dass sie niemals Elementen mit Klassennamen entsprechen, die diese Teilzeichenfolgen enthalten, aber nicht mit ihnen übereinstimmen. – BoltClock

Antwort

1

Es gibt ein speziell für das Problem entwickeltes Paket namens node-css-selector-parser, dem der "how to use it" -Teil fehlt, um die Klassennamen zu extrahieren. Die Lücke füllen, hier ist, wie ich es auf das Problem angewendet habe.

Mit node-css-selector-parser können wir einen CSS-Selektor und basierend auf einem Ergebnis Typ analysieren Klassennamen verwendet, die mit einem Punkt (zB .myclass) und Klassennamen verwendet innerhalb des Attributselektor (zB [class*=test]) analysieren:

// 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) { 
     classNames.push.apply(classNames, extractClassNames(selector.rule)) 
    }) 
    } 
    return classNames 
} 

(standard Code-Stil wird verwendet - daher zum Beispiel keine ; am Ende der Linien)

Dies erwies sich für mich arbeiten und hoffentlich würde anderen mit einem ähnlichen Problem helfen. Beachten Sie, dass dieser Code in diesem Zustand auch die Teilklassenwerte extrahiert, die an die ^=, $= und *= übergeben wurden, die idealerweise übersprungen werden müssen.