Skip to content

Commit 01eba03

Browse files
committed
dom4 v2
* removed `query` and `queryAll` not standard anymore * added support for CSS `:scope` selector
1 parent 931d6c7 commit 01eba03

File tree

8 files changed

+135
-187
lines changed

8 files changed

+135
-187
lines changed

.travis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
language: node_js
22
node_js:
3-
- 5
3+
- stable
44
git:
55
depth: 1
66
branches:

README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ A fully tested and covered polyfill for new DOM Level 4 entries
99
Many thanks to [cdnjs](http://www.cdnjs.com) for hosting this script. Following an example on how to include it.
1010
```html
1111
<script
12-
src="//cdnjs.cloudflare.com/ajax/libs/dom4/1.8.3/dom4.js"
12+
src="//cdnjs.cloudflare.com/ajax/libs/dom4/2.0.0/dom4.js"
1313
>/* DOM4 */</script>
1414
```
1515

16+
### New v2
17+
18+
Both `query` and `queryAll` have been removed, while CSS `:scope` selector has been added.
19+
1620

1721
## Features
1822
This is a [fully tested](http://webreflection.github.io/dom4/test/) and covered polyfill for both [new DOM Level 4 parentNode entries](https://dom.spec.whatwg.org/#parentnode):
@@ -31,10 +35,9 @@ The [implemented test](https://github.com/WebReflection/dom4/blob/master/test/do
3135

3236
Other fixes/standardized behaviors include:
3337

34-
* **new** [DOM Listener: capture, passive, and once](https://www.webreflection.co.uk/blog/2016/04/17/new-dom4-standards)
38+
* [DOM Listener: capture, passive, and once](https://www.webreflection.co.uk/blog/2016/04/17/new-dom4-standards)
3539
* fully normalized `KeyboardEvent`, `MouseEvent` and the latest way to create `new Event('type')`
36-
* [Elements as Array subclass](http://www.w3.org/TR/2015/WD-dom-20150428/#elements), so that `el.query` is the relative equivalent of `el.querySelector` and `el.queryAll` is the relative equivalent of `Array.prototype.slice.call(el.querySelectorAll(css))`. Relative means `node.query('body p')` won't return any `p` element contained in `node`, while old `querySelector` would.
37-
* [DROPPED PROPOSAL](http://stackoverflow.com/questions/23269785/whats-the-difference-between-queryall-and-queryselectorall/38245620#38245620) **document.queryAll(css)** returns an Array so there's no need to use `Array.prototype.slice.call(document.querySelectorAll(str))` or `[...document.querySelectorAll(str)]` or `Array.from(document.querySelectorAll(css))`, just `queryAll` for a list or `query` to get one element. We can forget the `Selector` bit \o/
40+
* CSS `:scope` selector for any HTML Element (no `document` since useless, sorry)
3841
* [classList](http://www.w3.org/TR/dom/#domtokenlist), with forced fixes for iOS 5.1 and Nokia ASHA Xpress Browser and early implementations
3942
* [CustomEvent](http://www.w3.org/TR/dom/#customevent) constructor for all browsers down to IE8
4043
* [Element#matches](https://dom.spec.whatwg.org/#dom-element-matches) utility to test elements against CSS selectors

build/dom4.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/dom4.max.js

+55-67
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@ THE SOFTWARE.
121121
},
122122
set: function(){}
123123
},
124-
uid = 'dom4-tmp-'.concat(Math.random() * +new Date()).replace('.','-'),
125124
trim = /^\s+|\s+$/g,
126125
spaces = /\s+/,
127126
SPACE = '\x20',
@@ -148,50 +147,7 @@ THE SOFTWARE.
148147
ElementPrototype = (window.Element || Node || window.HTMLElement).prototype,
149148
HTMLSelectElement = window.HTMLSelectElement || createElement('select').constructor,
150149
selectRemove = HTMLSelectElement.prototype.remove,
151-
ShadowRoot = window.ShadowRoot,
152150
SVGElement = window.SVGElement,
153-
// normalizes multiple ids as CSS query
154-
idSpaceFinder = / /g,
155-
idSpaceReplacer = '\\ ',
156-
createQueryMethod = function (methodName) {
157-
var createArray = methodName === 'querySelectorAll';
158-
return function (css) {
159-
var a, i, id, query, nl, selectors, node = this.parentNode;
160-
if (node) {
161-
for (
162-
id = this.getAttribute('id') || uid,
163-
query = id === uid ? id : id.replace(idSpaceFinder, idSpaceReplacer),
164-
selectors = css.split(','),
165-
i = 0; i < selectors.length; i++
166-
) {
167-
selectors[i] = '#' + query + ' ' + selectors[i];
168-
}
169-
css = selectors.join(',');
170-
}
171-
if (id === uid) this.setAttribute('id', id);
172-
nl = (node || this)[methodName](css);
173-
if (id === uid) this.removeAttribute('id');
174-
// return a list
175-
if (createArray) {
176-
i = nl.length;
177-
a = new Array(i);
178-
while (i--) a[i] = nl[i];
179-
}
180-
// return node or null
181-
else {
182-
a = nl;
183-
}
184-
return a;
185-
};
186-
},
187-
addQueryAndAll = function (where) {
188-
if (!('query' in where)) {
189-
where.query = ElementPrototype.query;
190-
}
191-
if (!('queryAll' in where)) {
192-
where.queryAll = ElementPrototype.queryAll;
193-
}
194-
},
195151
properties = [
196152
'matches', (
197153
ElementPrototype.matchesSelector ||
@@ -269,9 +225,7 @@ THE SOFTWARE.
269225
if (parentNode) {
270226
parentNode.removeChild(this);
271227
}
272-
},
273-
'query', createQueryMethod('querySelector'),
274-
'queryAll', createQueryMethod('querySelectorAll')
228+
}
275229
],
276230
slice = properties.slice,
277231
i = properties.length; i; i -= 2
@@ -311,23 +265,6 @@ THE SOFTWARE.
311265
}
312266
}
313267

314-
// bring query and queryAll to the document too
315-
addQueryAndAll(document);
316-
317-
// brings query and queryAll to fragments as well
318-
if (DocumentFragmentPrototype) {
319-
addQueryAndAll(DocumentFragmentPrototype);
320-
} else {
321-
try {
322-
addQueryAndAll(createDocumentFragment().constructor.prototype);
323-
} catch(o_O) {}
324-
}
325-
326-
// bring query and queryAll to the ShadowRoot too
327-
if (ShadowRoot) {
328-
addQueryAndAll(ShadowRoot.prototype);
329-
}
330-
331268
// most likely an IE9 only issue
332269
// see https://github.com/WebReflection/dom4/issues/6
333270
if (!createElement('a').matches('a')) {
@@ -757,6 +694,58 @@ THE SOFTWARE.
757694
if (MouseEvent !== o_O) MouseEvent = o_O;
758695
}
759696

697+
if (!document.querySelectorAll('*').forEach) {
698+
(function () {
699+
function patch(what) {
700+
var querySelectorAll = what.querySelectorAll;
701+
what.querySelectorAll = function qSA(css) {
702+
var result = querySelectorAll.call(this, css);
703+
result.forEach = Array.prototype.forEach;
704+
return result;
705+
};
706+
}
707+
patch(document);
708+
patch(HTMLElement.prototype);
709+
}());
710+
}
711+
712+
try {
713+
// https://drafts.csswg.org/selectors-4/#the-scope-pseudo
714+
document.querySelector(':scope *');
715+
} catch(o_O) {
716+
(function () {
717+
var counter = 0;
718+
var parent = createElement('div');
719+
var prefix = 'scope-' + (Math.random() * 1e9 >>> 0) + '-';
720+
var proto = HTMLElement.prototype;
721+
var querySelector = proto.querySelector;
722+
var querySelectorAll = proto.querySelectorAll;
723+
proto.querySelector = function qS(css) {
724+
return find(this, querySelector, css);
725+
};
726+
proto.querySelectorAll = function qSA(css) {
727+
return find(this, querySelectorAll, css);
728+
};
729+
function find(node, method, css) {
730+
var oldID = node.id;
731+
var noParent = !node.parentNode;
732+
node.id = oldID || (prefix + counter++);
733+
if (noParent) parent.appendChild(node);
734+
var result = method.call(
735+
node.parentNode,
736+
css.replace(
737+
/(^|,\s*)(:scope([ >]|$))?/g,
738+
function ($0, $1, $2, $3) {
739+
return $1 + '#' + node.id + ($3 || ' ');
740+
}
741+
)
742+
);
743+
if (noParent) parent.removeChild(node);
744+
node.id = oldID;
745+
return result;
746+
}
747+
}());
748+
}
760749
}(window));(function (global){'use strict';
761750

762751
// a WeakMap fallback for DOM nodes only used as key
@@ -861,7 +850,6 @@ THE SOFTWARE.
861850

862851
var
863852
Event = global.CustomEvent,
864-
hOP = Object.prototype.hasOwnProperty,
865853
dE = global.dispatchEvent,
866854
aEL = global.addEventListener,
867855
rEL = global.removeEventListener,
@@ -883,7 +871,7 @@ THE SOFTWARE.
883871
options.once ? '1' : '0'
884872
);
885873
},
886-
augment, proto
874+
augment
887875
;
888876

889877
try {
@@ -980,4 +968,4 @@ THE SOFTWARE.
980968
}());
981969
}
982970

983-
}(self));
971+
}(self));

index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
];
1414
</script>
1515
<script>// don't change code here
16-
for(var i = 0; i < TESTS.length; document.write('<script src="src/' + TESTS[i++] + '.js"><' + '/script>'));
16+
for(var i = 0; i < TESTS.length; document.write('<script src="build/' + TESTS[i++] + '.max.js?_=' + Math.random() + '"><' + '/script>'));
1717
function wru(wru){
1818
var
1919
all = [],

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "dom4",
33
"title": "DOM4",
44
"description": "a fully tested and covered polyfill for new DOM Level 4 entries",
5-
"version": "1.8.5",
5+
"version": "2.0.0",
66
"main": "build/dom4.max.js",
77
"homepage": "https://github.com/WebReflection/dom4",
88
"author": {

src/dom4.js

+53-64
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@
9999
},
100100
set: function(){}
101101
},
102-
uid = 'dom4-tmp-'.concat(Math.random() * +new Date()).replace('.','-'),
103102
trim = /^\s+|\s+$/g,
104103
spaces = /\s+/,
105104
SPACE = '\x20',
@@ -126,50 +125,7 @@
126125
ElementPrototype = (window.Element || Node || window.HTMLElement).prototype,
127126
HTMLSelectElement = window.HTMLSelectElement || createElement('select').constructor,
128127
selectRemove = HTMLSelectElement.prototype.remove,
129-
ShadowRoot = window.ShadowRoot,
130128
SVGElement = window.SVGElement,
131-
// normalizes multiple ids as CSS query
132-
idSpaceFinder = / /g,
133-
idSpaceReplacer = '\\ ',
134-
createQueryMethod = function (methodName) {
135-
var createArray = methodName === 'querySelectorAll';
136-
return function (css) {
137-
var a, i, id, query, nl, selectors, node = this.parentNode;
138-
if (node) {
139-
for (
140-
id = this.getAttribute('id') || uid,
141-
query = id === uid ? id : id.replace(idSpaceFinder, idSpaceReplacer),
142-
selectors = css.split(','),
143-
i = 0; i < selectors.length; i++
144-
) {
145-
selectors[i] = '#' + query + ' ' + selectors[i];
146-
}
147-
css = selectors.join(',');
148-
}
149-
if (id === uid) this.setAttribute('id', id);
150-
nl = (node || this)[methodName](css);
151-
if (id === uid) this.removeAttribute('id');
152-
// return a list
153-
if (createArray) {
154-
i = nl.length;
155-
a = new Array(i);
156-
while (i--) a[i] = nl[i];
157-
}
158-
// return node or null
159-
else {
160-
a = nl;
161-
}
162-
return a;
163-
};
164-
},
165-
addQueryAndAll = function (where) {
166-
if (!('query' in where)) {
167-
where.query = ElementPrototype.query;
168-
}
169-
if (!('queryAll' in where)) {
170-
where.queryAll = ElementPrototype.queryAll;
171-
}
172-
},
173129
properties = [
174130
'matches', (
175131
ElementPrototype.matchesSelector ||
@@ -247,9 +203,7 @@
247203
if (parentNode) {
248204
parentNode.removeChild(this);
249205
}
250-
},
251-
'query', createQueryMethod('querySelector'),
252-
'queryAll', createQueryMethod('querySelectorAll')
206+
}
253207
],
254208
slice = properties.slice,
255209
i = properties.length; i; i -= 2
@@ -289,23 +243,6 @@
289243
}
290244
}
291245

292-
// bring query and queryAll to the document too
293-
addQueryAndAll(document);
294-
295-
// brings query and queryAll to fragments as well
296-
if (DocumentFragmentPrototype) {
297-
addQueryAndAll(DocumentFragmentPrototype);
298-
} else {
299-
try {
300-
addQueryAndAll(createDocumentFragment().constructor.prototype);
301-
} catch(o_O) {}
302-
}
303-
304-
// bring query and queryAll to the ShadowRoot too
305-
if (ShadowRoot) {
306-
addQueryAndAll(ShadowRoot.prototype);
307-
}
308-
309246
// most likely an IE9 only issue
310247
// see https://github.com/WebReflection/dom4/issues/6
311248
if (!createElement('a').matches('a')) {
@@ -735,4 +672,56 @@
735672
if (MouseEvent !== o_O) MouseEvent = o_O;
736673
}
737674

675+
if (!document.querySelectorAll('*').forEach) {
676+
(function () {
677+
function patch(what) {
678+
var querySelectorAll = what.querySelectorAll;
679+
what.querySelectorAll = function qSA(css) {
680+
var result = querySelectorAll.call(this, css);
681+
result.forEach = Array.prototype.forEach;
682+
return result;
683+
};
684+
}
685+
patch(document);
686+
patch(HTMLElement.prototype);
687+
}());
688+
}
689+
690+
try {
691+
// https://drafts.csswg.org/selectors-4/#the-scope-pseudo
692+
document.querySelector(':scope *');
693+
} catch(o_O) {
694+
(function () {
695+
var counter = 0;
696+
var parent = createElement('div');
697+
var prefix = 'scope-' + (Math.random() * 1e9 >>> 0) + '-';
698+
var proto = HTMLElement.prototype;
699+
var querySelector = proto.querySelector;
700+
var querySelectorAll = proto.querySelectorAll;
701+
proto.querySelector = function qS(css) {
702+
return find(this, querySelector, css);
703+
};
704+
proto.querySelectorAll = function qSA(css) {
705+
return find(this, querySelectorAll, css);
706+
};
707+
function find(node, method, css) {
708+
var oldID = node.id;
709+
var noParent = !node.parentNode;
710+
node.id = oldID || (prefix + counter++);
711+
if (noParent) parent.appendChild(node);
712+
var result = method.call(
713+
node.parentNode,
714+
css.replace(
715+
/(^|,\s*)(:scope([ >]|$))?/g,
716+
function ($0, $1, $2, $3) {
717+
return $1 + '#' + node.id + ($3 || ' ');
718+
}
719+
)
720+
);
721+
if (noParent) parent.removeChild(node);
722+
node.id = oldID;
723+
return result;
724+
}
725+
}());
726+
}
738727
}(window));

0 commit comments

Comments
 (0)