2 Behaviour v1.1 by Ben Nolan, June 2005. Based largely on the work
3 of Simon Willison (see comments by Simon below).
7 Uses css selectors to apply javascript behaviours to enable
8 unobtrusive javascript in html documents.
13 'b.someclass' : function(element){
14 element.onclick = function(){
15 alert(this.innerHTML);
18 '#someid u' : function(element){
19 element.onmouseover = function(){
20 this.innerHTML = "BLAH!";
25 Behaviour.register(myrules);
27 // Call Behaviour.apply() to re-apply the rules (if you
28 // update the dom, etc).
32 This file is entirely BSD licensed.
36 http://ripcord.co.nz/behaviour/
43 register : function(sheet){
44 Behaviour.list.push(sheet);
48 Behaviour.addLoadEvent(function(){
54 for (h=0;sheet=Behaviour.list[h];h++){
55 for (selector in sheet){
56 list = document.getElementsBySelector(selector);
62 for (i=0;element=list[i];i++){
63 sheet[selector](element);
69 addLoadEvent : function(func){
70 var oldonload = window.onload;
72 if (typeof window.onload != 'function') {
75 window.onload = function() {
86 The following code is Copyright (C) Simon Willison 2004.
88 document.getElementsBySelector(selector)
89 - returns an array of element objects from the current document
90 matching the CSS selector. Selectors can contain element names,
91 class names and ids and can be nested. For example:
93 elements = document.getElementsBySelect('div#main p a.external')
95 Will return an array of all 'a' elements with 'external' in their
96 class attribute that are contained inside 'p' elements that are
97 contained inside the 'div' element which has id="main"
99 New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
100 See http://www.w3.org/TR/css3-selectors/#attribute-selectors
102 Version 0.4 - Simon Willison, March 25th 2003
103 -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
107 function getAllChildren(e) {
108 // Returns all children of element. Workaround required for IE5/Windows. Ugh.
109 return e.all ? e.all : e.getElementsByTagName('*');
112 document.getElementsBySelector = function(selector) {
113 // Attempt to fail gracefully in lesser browsers
114 if (!document.getElementsByTagName) {
117 // Split selector in to tokens
118 var tokens = selector.split(' ');
119 var currentContext = new Array(document);
120 for (var i = 0; i < tokens.length; i++) {
121 token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
122 if (token.indexOf('#') > -1) {
123 // Token is an ID selector
124 var bits = token.split('#');
125 var tagName = bits[0];
127 var element = document.getElementById(id);
128 if (tagName && element.nodeName.toLowerCase() != tagName) {
129 // tag with that ID not found, return false
132 // Set currentContext to contain just this element
133 currentContext = new Array(element);
134 continue; // Skip to next token
136 if (token.indexOf('.') > -1) {
137 // Token contains a class selector
138 var bits = token.split('.');
139 var tagName = bits[0];
140 var className = bits[1];
144 // Get elements matching tag, filter them for class selector
145 var found = new Array;
147 for (var h = 0; h < currentContext.length; h++) {
149 if (tagName == '*') {
150 elements = getAllChildren(currentContext[h]);
152 elements = currentContext[h].getElementsByTagName(tagName);
154 for (var j = 0; j < elements.length; j++) {
155 found[foundCount++] = elements[j];
158 currentContext = new Array;
159 var currentContextIndex = 0;
160 for (var k = 0; k < found.length; k++) {
161 if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
162 currentContext[currentContextIndex++] = found[k];
165 continue; // Skip to next token
167 // Code to deal with attribute selectors
168 /* Original reg expression /^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
169 was replaced by new RegExp() cuz compressor fault */
170 if (token.match(new RegExp('^(\\w*)\\[(\\w+)([=~\\|\\^\\$\\*]?)=?"?([^\\]"]*)"?\\]$'))) {
171 var tagName = RegExp.$1;
172 var attrName = RegExp.$2;
173 var attrOperator = RegExp.$3;
174 var attrValue = RegExp.$4;
178 // Grab all of the tagName elements within current context
179 var found = new Array;
181 for (var h = 0; h < currentContext.length; h++) {
183 if (tagName == '*') {
184 elements = getAllChildren(currentContext[h]);
186 elements = currentContext[h].getElementsByTagName(tagName);
188 for (var j = 0; j < elements.length; j++) {
189 found[foundCount++] = elements[j];
192 currentContext = new Array;
193 var currentContextIndex = 0;
194 var checkFunction; // This function will be used to filter the elements
195 switch (attrOperator) {
196 case '=': // Equality
197 checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
199 case '~': // Match one of space seperated words
200 checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
202 case '|': // Match start with value followed by optional hyphen
203 checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
205 case '^': // Match starts with value
206 checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
208 case '$': // Match ends with value - fails with "Warning" in Opera 7
209 checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
211 case '*': // Match ends with value
212 checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
215 // Just test for existence of attribute
216 checkFunction = function(e) { return e.getAttribute(attrName); };
218 currentContext = new Array;
219 var currentContextIndex = 0;
220 for (var k = 0; k < found.length; k++) {
221 if (checkFunction(found[k])) {
222 currentContext[currentContextIndex++] = found[k];
225 // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
226 continue; // Skip to next token
229 if (!currentContext[0]){
233 // If we get here, token is JUST an element (not a class or ID selector)
235 var found = new Array;
237 for (var h = 0; h < currentContext.length; h++) {
238 var elements = currentContext[h].getElementsByTagName(tagName);
239 for (var j = 0; j < elements.length; j++) {
240 found[foundCount++] = elements[j];
243 currentContext = found;
245 return currentContext;
248 /* That revolting regular expression explained
249 /^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
250 \---/ \---/\-------------/ \-------/