1
/* document.getElementsBySelector(selector)
2
- returns an array of element objects from the current document
3
matching the CSS selector. Selectors can contain element names,
4
class names and ids and can be nested. For example:
6
elements = document.getElementsBySelect('div#main p a.external')
8
Will return an array of all 'a' elements with 'external' in their
9
class attribute that are contained inside 'p' elements that are
10
contained inside the 'div' element which has id="main"
12
New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
13
See http://www.w3.org/TR/css3-selectors/#attribute-selectors
15
Version 0.4 - Simon Willison, March 25th 2003
16
-- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
20
function getAllChildren(e) {
21
// Returns all children of element. Workaround required for IE5/Windows. Ugh.
22
return e.all ? e.all : e.getElementsByTagName('*');
25
document.getElementsBySelector = function(selector) {
26
// Attempt to fail gracefully in lesser browsers
27
if (!document.getElementsByTagName) {
30
// Split selector in to tokens
31
var tokens = selector.split(' ');
32
var currentContext = new Array(document);
33
for (var i = 0; i < tokens.length; i++) {
34
token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
35
if (token.indexOf('#') > -1) {
36
// Token is an ID selector
37
var bits = token.split('#');
38
var tagName = bits[0];
40
var element = document.getElementById(id);
41
if (!element || (tagName && element.nodeName.toLowerCase() != tagName)) {
42
// ID not found or tag with that ID not found, return false.
45
// Set currentContext to contain just this element
46
currentContext = new Array(element);
47
continue; // Skip to next token
49
if (token.indexOf('.') > -1) {
50
// Token contains a class selector
51
var bits = token.split('.');
52
var tagName = bits[0];
53
var className = bits[1];
57
// Get elements matching tag, filter them for class selector
58
var found = new Array;
60
for (var h = 0; h < currentContext.length; h++) {
63
elements = getAllChildren(currentContext[h]);
66
elements = currentContext[h].getElementsByTagName(tagName);
72
for (var j = 0; j < elements.length; j++) {
73
found[foundCount++] = elements[j];
76
currentContext = new Array;
77
var currentContextIndex = 0;
78
for (var k = 0; k < found.length; k++) {
79
if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
80
currentContext[currentContextIndex++] = found[k];
83
continue; // Skip to next token
85
// Code to deal with attribute selectors
86
if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
87
var tagName = RegExp.$1;
88
var attrName = RegExp.$2;
89
var attrOperator = RegExp.$3;
90
var attrValue = RegExp.$4;
94
// Grab all of the tagName elements within current context
95
var found = new Array;
97
for (var h = 0; h < currentContext.length; h++) {
100
elements = getAllChildren(currentContext[h]);
102
elements = currentContext[h].getElementsByTagName(tagName);
104
for (var j = 0; j < elements.length; j++) {
105
found[foundCount++] = elements[j];
108
currentContext = new Array;
109
var currentContextIndex = 0;
110
var checkFunction; // This function will be used to filter the elements
111
switch (attrOperator) {
112
case '=': // Equality
113
checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
115
case '~': // Match one of space seperated words
116
checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
118
case '|': // Match start with value followed by optional hyphen
119
checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
121
case '^': // Match starts with value
122
checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
124
case '$': // Match ends with value - fails with "Warning" in Opera 7
125
checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
127
case '*': // Match ends with value
128
checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
131
// Just test for existence of attribute
132
checkFunction = function(e) { return e.getAttribute(attrName); };
134
currentContext = new Array;
135
var currentContextIndex = 0;
136
for (var k = 0; k < found.length; k++) {
137
if (checkFunction(found[k])) {
138
currentContext[currentContextIndex++] = found[k];
141
// alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
142
continue; // Skip to next token
144
// If we get here, token is JUST an element (not a class or ID selector)
146
var found = new Array;
148
for (var h = 0; h < currentContext.length; h++) {
149
var elements = currentContext[h].getElementsByTagName(tagName);
150
for (var j = 0; j < elements.length; j++) {
151
found[foundCount++] = elements[j];
154
currentContext = found;
156
return currentContext;
159
/* That revolting regular expression explained
160
/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
161
\---/ \---/\-------------/ \-------/