N
nick
Hope this gets through, I know some of you guys ignore google groups
posts due to all the spam.
Anyway, I know everyone and their mother has done a query selector
thing, but here's my take on it. I needed something simple and
lightweight for in-house use. I'd like to get some feedback from the
gurus here.
Mostly, I'm wondering if I've made any glaring mistakes that will
prevent this from working in certain browsers/situations. Advice on
coding style and optimization hints are also welcome.
The "select" function takes a CSS selector string. Currently selecting
tag names, class names and IDs is suported. These should work in any
combination supported by CSS. A selector like this should work: "body
#nav a.offsite, #footer .inner a"
///
var DM = DM || {};
(function(){
DM.contains = function(obj, value) {
for (var i=-1, len=obj.length; i<len; i++) {
if (obj===value) return true;
}
return false;
};
DM.unique = function(obj) {
for (var i=0, len=obj.length, r=[]; i<len; i++) {
if (!(DM.contains(r, obj))) r.push(obj);
}
return r;
};
DM.append = function(a, b) {
for (var i=0, len=b.length; i<len; i++) a.push(b);
return a;
},
DM.select = function(selector) {
var groups=selector.split(','), i=-1, group,
found=[], nodes=[document];
while (group=groups[++i]) {
var path=group.split(' '), j=-1, part;
while (part=path[++j]) {
nodes=selectPart(nodes, part)
}
found=DM.unique((DM.append(found, nodes)));
}
return found;
};
function selectPart(nodes, selector) {
var i=-1, symbol, attr, val, buffer='', tokens=[];
while (symbol=selector.charAt(++i)) {
switch (symbol) {
case '#':
if (i) tokens.push(buffer);
buffer='';
tokens.push('id');
break;
case '.':
if (i) tokens.push(buffer);
buffer='';
tokens.push('class');
break;
default :
if (!i) tokens.push('tagName');
buffer+=symbol;
break;
}
}
tokens.push(buffer);
i=-1;
while ((attr=tokens[++i]) && (val=tokens[++i])) {
nodes = byAttr(nodes, attr, val, i-1);
}
return nodes;
}
function getDescendants(nodes, includeSiblings) {
var node, results=[];
for (var i=0, len=nodes.length; i<len; i++) {
node=nodes;
DM.append(results, node.getElementsByTagName('*'));
if (includeSiblings) results.push(node);
}
return results;
}
function byAttr(nodes, attribute, value, includeSiblings) {
var results=[], desc=getDescendants(nodes, includeSiblings);
value=value.toLowerCase();
for (var i=0, len=desc.length; i<len; i++) {
if (((''+desc[attribute]).toLowerCase()==value) ||
((''+desc[attribute+'Name']).toLowerCase()==value)) {
results.push(desc);
}
}
return results;
}
})();
///
P.S. - our company's initials are DM, so I'm "namespacing" things into
an object named DM.
posts due to all the spam.
Anyway, I know everyone and their mother has done a query selector
thing, but here's my take on it. I needed something simple and
lightweight for in-house use. I'd like to get some feedback from the
gurus here.
Mostly, I'm wondering if I've made any glaring mistakes that will
prevent this from working in certain browsers/situations. Advice on
coding style and optimization hints are also welcome.
The "select" function takes a CSS selector string. Currently selecting
tag names, class names and IDs is suported. These should work in any
combination supported by CSS. A selector like this should work: "body
#nav a.offsite, #footer .inner a"
///
var DM = DM || {};
(function(){
DM.contains = function(obj, value) {
for (var i=-1, len=obj.length; i<len; i++) {
if (obj===value) return true;
}
return false;
};
DM.unique = function(obj) {
for (var i=0, len=obj.length, r=[]; i<len; i++) {
if (!(DM.contains(r, obj))) r.push(obj);
}
return r;
};
DM.append = function(a, b) {
for (var i=0, len=b.length; i<len; i++) a.push(b);
return a;
},
DM.select = function(selector) {
var groups=selector.split(','), i=-1, group,
found=[], nodes=[document];
while (group=groups[++i]) {
var path=group.split(' '), j=-1, part;
while (part=path[++j]) {
nodes=selectPart(nodes, part)
}
found=DM.unique((DM.append(found, nodes)));
}
return found;
};
function selectPart(nodes, selector) {
var i=-1, symbol, attr, val, buffer='', tokens=[];
while (symbol=selector.charAt(++i)) {
switch (symbol) {
case '#':
if (i) tokens.push(buffer);
buffer='';
tokens.push('id');
break;
case '.':
if (i) tokens.push(buffer);
buffer='';
tokens.push('class');
break;
default :
if (!i) tokens.push('tagName');
buffer+=symbol;
break;
}
}
tokens.push(buffer);
i=-1;
while ((attr=tokens[++i]) && (val=tokens[++i])) {
nodes = byAttr(nodes, attr, val, i-1);
}
return nodes;
}
function getDescendants(nodes, includeSiblings) {
var node, results=[];
for (var i=0, len=nodes.length; i<len; i++) {
node=nodes;
DM.append(results, node.getElementsByTagName('*'));
if (includeSiblings) results.push(node);
}
return results;
}
function byAttr(nodes, attribute, value, includeSiblings) {
var results=[], desc=getDescendants(nodes, includeSiblings);
value=value.toLowerCase();
for (var i=0, len=desc.length; i<len; i++) {
if (((''+desc[attribute]).toLowerCase()==value) ||
((''+desc[attribute+'Name']).toLowerCase()==value)) {
results.push(desc);
}
}
return results;
}
})();
///
P.S. - our company's initials are DM, so I'm "namespacing" things into
an object named DM.