patternjavascriptMinor
querySelectorAll shim for non-IE browsers
Viewed 0 times
queryselectorallnonbrowsersforshim
Problem
Every shim I have seen for this function has used the IE-only CSS
jQuery is out of the question, since I don't want to get a whole framework just to shim one tiny little function. So I went and wrote my own. I'd like to submit it for review, which may include optimisations.
Please note that this is not a complete shim, only enough for what is frequently used:
The code:
```
if( typeof document.querySelector == "undefined") {
document.querySelectorAll = function(sel) {
var sels = sel.split(","),
run = function(node,selector) {
var sel = selector.split(/[ >]+/), com = selector.match(/[ >]+/g) || [], s, c, ret = [node], nodes, l, i, subs, m, j, check, x, w, ok,
as;
com.unshift(" ");
while(s = sel.shift()) {
c = com.shift();
if( c) c = c.replace(/^ +| +$/g,"");
nodes = ret.slice(0);
ret = [];
l = nodes.length;
subs = s.match(/[#.[]?[a-z_-]+(?:='[^']+'|="[^"]+")?]?/gi);
m = subs.length;
for( i=0; i" ? nodes[i].children : nodes[i].getElementsByTagName("*");
if( !check) continue;
w = check.length;
for( x=0; x 0) return ret[0];
else return null;
};
if( typeof HTMLElement != "undefined") {
HTMLElement.prototype.querySelector = document.querySelector;
HTMLElement.prototype.querySelectorAll = document.querySelectorAll;
}
else {
dommods_extend.push(function() {
var a = document.getElementsByTagName("*"), l = a.length,
expression trick. While there's no denying it's effective, it's made useless if the browser is not IE and doesn't support querySelectorAll either.jQuery is out of the question, since I don't want to get a whole framework just to shim one tiny little function. So I went and wrote my own. I'd like to submit it for review, which may include optimisations.
Please note that this is not a complete shim, only enough for what is frequently used:
- tag name
- id
- class name
- attribute, presence and exact value match
- descendant combinator
- child combinator
The code:
```
if( typeof document.querySelector == "undefined") {
document.querySelectorAll = function(sel) {
var sels = sel.split(","),
run = function(node,selector) {
var sel = selector.split(/[ >]+/), com = selector.match(/[ >]+/g) || [], s, c, ret = [node], nodes, l, i, subs, m, j, check, x, w, ok,
as;
com.unshift(" ");
while(s = sel.shift()) {
c = com.shift();
if( c) c = c.replace(/^ +| +$/g,"");
nodes = ret.slice(0);
ret = [];
l = nodes.length;
subs = s.match(/[#.[]?[a-z_-]+(?:='[^']+'|="[^"]+")?]?/gi);
m = subs.length;
for( i=0; i" ? nodes[i].children : nodes[i].getElementsByTagName("*");
if( !check) continue;
w = check.length;
for( x=0; x 0) return ret[0];
else return null;
};
if( typeof HTMLElement != "undefined") {
HTMLElement.prototype.querySelector = document.querySelector;
HTMLElement.prototype.querySelectorAll = document.querySelectorAll;
}
else {
dommods_extend.push(function() {
var a = document.getElementsByTagName("*"), l = a.length,
Solution
Overal
I am not sure which non IE-browser doesn't support querySelectorAll. Regardless, the code seems very performant, if a little Golfic ( reminds me of code golf ).
Nitpickings
If you wish this source to be maintained / debugged /reviewed by others, there are some things you can change to make that easier:
-
Declare unassigned variables last. It is easier on the eyes to know when to stop reading that long var line.
-
I am all for Spartan coding, but w,m,c (which is not a character!), l etc. just make for unmaintainable code.
-
Make your regex's constants with a meaningful name, it will make your code easier to understand.
-
Use new lines after the
-
if/else branches should either both have curly braces, or both not have curly braces, dont mix
-
You could use
Finally
It is considered bad practice to shim in incomplete implementations, if you want to add to the HTMLElement prototype, use your own names to remove any confusion.
I am not sure which non IE-browser doesn't support querySelectorAll. Regardless, the code seems very performant, if a little Golfic ( reminds me of code golf ).
Nitpickings
If you wish this source to be maintained / debugged /reviewed by others, there are some things you can change to make that easier:
-
Declare unassigned variables last. It is easier on the eyes to know when to stop reading that long var line.
-
I am all for Spartan coding, but w,m,c (which is not a character!), l etc. just make for unmaintainable code.
-
Make your regex's constants with a meaningful name, it will make your code easier to understand.
-
Use new lines after the
if condition-
if/else branches should either both have curly braces, or both not have curly braces, dont mix
-
You could use
Array.concat here://As is
for( i=0; i<l; i++) {
tmp = run(this,sels[i]);
m = tmp.length;
for( j=0; j<m; j++) {
ret.push(tmp[j]);
}
}
//Replacement proposal
for( i=0; i<l; i++)
ret.push( run(this,sels[i]) );- You could use a trinary here :
//As is
if( ret.length > 0) return ret[0];
else return null
//Replacement proposal
return ret.length?ret[0]:null;Finally
It is considered bad practice to shim in incomplete implementations, if you want to add to the HTMLElement prototype, use your own names to remove any confusion.
HTMLElement.prototype.darkQuerySelector sounds so much better anyway ;)Code Snippets
//As is
for( i=0; i<l; i++) {
tmp = run(this,sels[i]);
m = tmp.length;
for( j=0; j<m; j++) {
ret.push(tmp[j]);
}
}
//Replacement proposal
for( i=0; i<l; i++)
ret.push( run(this,sels[i]) );//As is
if( ret.length > 0) return ret[0];
else return null
//Replacement proposal
return ret.length?ret[0]:null;Context
StackExchange Code Review Q#12444, answer score: 2
Revisions (0)
No revisions yet.