Skip to content

Commit f6189ae

Browse files
committed
add :not pseudo-class
Close #1.
1 parent 896d376 commit f6189ae

File tree

4 files changed

+38
-4
lines changed

4 files changed

+38
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ That's it!
5959
- [x] Begins with: `[value^="prefix"]`
6060
- [x] Containment: `[value*="substr"]`
6161
- [x] Ends with: `[value$="suffix"]`
62+
- [x] Negation pseudo-class: `*:not(paragraph)`
6263

6364
## API
6465

lib/select.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,13 +78,14 @@ select.rule = function (selector, ast) {
7878

7979
// True if node matches head of selector rule.
8080
function matches (rule, node) {
81+
var match = true;
82+
8183
// Match type.
82-
if (rule.tagName && rule.tagName != '*' && node.type != rule.tagName) {
83-
return false;
84-
}
84+
match = match && (!rule.tagName || rule.tagName == '*' ||
85+
rule.tagName == node.type);
8586

8687
// Match attributes.
87-
return (rule.attrs || []).every(function (attr) {
88+
match = match && (rule.attrs || []).every(function (attr) {
8889
switch (attr.operator) {
8990
case undefined:
9091
return attr.name in node;
@@ -122,6 +123,19 @@ function matches (rule, node) {
122123
throw Error('Undefined attribute operator: ' + attr.operator);
123124
}
124125
});
126+
127+
// Match pseudo classes.
128+
match = match && (rule.pseudos || []).every(function (pseudo) {
129+
switch (pseudo.name) {
130+
case 'not':
131+
return !matches(pseudo.value.rule, node);
132+
133+
default:
134+
throw Error('Undefined pseudo-class: ' + pseudo.name);
135+
}
136+
});
137+
138+
return match;
125139
}
126140

127141

lib/selector.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ module.exports = function SelectorParser () {
77
var parser = new Parser;
88
parser.registerNestingOperators('>', '+', '~');
99
parser.registerAttrEqualityMods('^', '*', '$');
10+
parser.registerSelectorPseudos('not');
1011
return parser.parse.bind(parser);
1112
};

test/test.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,21 @@ test('attribute selectors', function (t) {
125125

126126
t.end();
127127
});
128+
129+
130+
test('negation pseudo-class', function (t) {
131+
t.deepEqual(select(ast, 'list:not([nonexistent])'), select(ast, 'list'));
132+
t.deepEqual(select(ast, 'list:not([start=null])'),
133+
select(ast, 'list[start=1]'));
134+
t.deepEqual(select(ast, 'heading text:not([value*=" "])')
135+
.map(function (node) { return node.value }),
136+
['Vitae', 'References', 'License']);
137+
t.deepEqual(select(ast, [
138+
'list:not([ordered=true])',
139+
'*:not(listItem):not(paragraph)[children]:not(list)'
140+
].join(' ')), [
141+
path(ast, [4, 1, 1, 1, 0, 0]),
142+
path(ast, [4, 1, 1, 1, 0, 0, 1])
143+
]);
144+
t.end();
145+
});

0 commit comments

Comments
 (0)