* Make document.getElementsByClassName match the WHATWG Web Applications 1.0 specification which was adopted in Firefox 3 (http://www.whatwg.org/specs/web-apps/current-work/#getelementsbyclassname). It now supports multiple class names given as an array or a space-separated list in a string. The method will only return the nodes that match all the class names. In browsers that implement the method natively it will not be overwritten. Closes #8401. [Mislav Marohnic]

This commit is contained in:
Thomas Fuchs 2007-06-17 22:26:46 +00:00
parent afbef44cd6
commit 03ae9dd3ee
4 changed files with 73 additions and 25 deletions

View File

@ -1,5 +1,11 @@
*SVN*
* Make document.getElementsByClassName match the WHATWG Web Applications 1.0 specification which was adopted in Firefox 3
(http://www.whatwg.org/specs/web-apps/current-work/#getelementsbyclassname). It now supports multiple class names given as an array or a space-separated list in a string. The method will only return the nodes that match all the class names. In browsers that implement the method natively it will not be overwritten. Closes #8401. [Mislav Marohnić]
Examples:
document.getElementsByClassName('foo bar')
document.getElementsByClassName(['foo', 'bar'])
* Fix a Safari rendering issue when floating elements could temporarily disappear when opacity was set to 1. Closes #7063. References #3044, #3813, #6706. [Thomas Fuchs, davidjrice]
* Prevent a crash in Safari when calling String#evalJSON(true) on very large strings. Add String#isJSON. Closes #7834. [Tobie Langel]

View File

@ -18,22 +18,7 @@ if (Prototype.BrowserFeatures.XPath) {
results.push(query.snapshotItem(i));
return results;
};
document.getElementsByClassName = function(className, parentElement) {
var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
return document._getElementsByXPath(q, parentElement);
}
} else document.getElementsByClassName = function(className, parentElement) {
var children = ($(parentElement) || document.body).getElementsByTagName('*');
var elements = [], child;
for (var i = 0, length = children.length; i < length; i++) {
child = children[i];
if (Element.hasClassName(child, className))
elements.push(Element.extend(child));
}
return elements;
};
}
/*--------------------------------------------------------------------------*/
@ -243,10 +228,6 @@ Element.Methods = {
return Selector.findChildElements(element, args);
},
getElementsByClassName: function(element, className) {
return document.getElementsByClassName(className, element);
},
readAttribute: function(element, name) {
element = $(element);
if (Prototype.Browser.IE) {
@ -614,6 +595,42 @@ Element.Methods = {
}
};
if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
function isArray(className) {
return className.constructor == Array || (/\s/.test(className) && !className.toString().blank());
}
function classNamesArray(classNames) {
return classNames.constructor == Array ? classNames : $w(classNames.toString());
}
function iter(name) {
return name.toString().blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
}
instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
function(element, className) {
var cond = isArray(className) ? classNamesArray(className).map(iter).join('') : iter(className);
return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
} : function(element, className) {
var elements = [], classNames = (isArray(className) ? classNamesArray(className) : null);
if (classNames ? !classNames.length : className.toString().blank()) return elements;
var nodes = $(element).getElementsByTagName('*');
className = ' ' + (classNames ? classNames.join(' ') : className) + ' ';
for (var i = 0, child, cn; child = nodes[i]; i++) {
if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
(classNames && classNames.all(function(name) {
return !name.toString().blank() && cn.include(' ' + name + ' ');
}))))
elements.push(Element.extend(child));
}
return elements;
};
return function(className, parentElement) {
return $(parentElement || document.body).getElementsByClassName(className);
};
}(Element.Methods);
Object.extend(Element.Methods, {
childElements: Element.Methods.immediateDescendants
});

View File

@ -484,7 +484,7 @@ Test.Unit.Assertions.prototype = {
assertElementsMatch: function() {
var expressions = $A(arguments), elements = $A(expressions.shift());
if (elements.length != expressions.length) {
this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions');
this.fail('assertElementsMatch: size mismatch: ' + elements.length + ' elements, ' + expressions.length + ' expressions (' + expressions.inspect() + ')');
return false;
}
elements.zip(expressions).all(function(pair, index) {

View File

@ -207,8 +207,10 @@
<ul class="A B" id="class_names_ul">
<li class="C"></li>
<li class="A C"></li>
<li class="1"></li>
</ul>
<div class="B C D"></div>
<div id="unextended"></div>
</div>
<div id="style_test_1" style="display:none;"></div>
@ -337,7 +339,9 @@
</div>
<div id="notInlineAbsoluted"></div>
<div id="inlineAbsoluted" style="position: absolute"></div>
<div id="unextended"></div>
<!-- Tests follow -->
<script type="text/javascript" language="javascript" charset="utf-8">
// <![CDATA[
@ -381,10 +385,31 @@
}},
testGetElementsByClassName: function() {with(this) {
var div = $('class_names'), list = $('class_names_ul');
assertElementsMatch(document.getElementsByClassName('A'), 'p.A', 'ul#class_names_ul.A', 'li.A.C');
assertElementsMatch(document.getElementsByClassName('A', 'class_names_ul'), 'li.A.C');
assertElementsMatch(document.getElementsByClassName('B', 'class_names'), 'ul#class_names_ul.A.B', 'div.B.C.D');
assertElementsMatch(document.getElementsByClassName('B', 'class_names_ul'));
if (Prototype.Browser.IE)
assertUndefined(document.getElementById('unextended').show);
assertElementsMatch(list.getElementsByClassName('A'), 'li.A.C');
assertElementsMatch(list.getElementsByClassName('C A'), 'li.A.C');
assertElementsMatch(div.getElementsByClassName('B'), 'ul#class_names_ul.A.B', 'div.B.C.D');
assertElementsMatch(div.getElementsByClassName('D C B'), 'div.B.C.D');
assertElementsMatch(div.getElementsByClassName($w('D C B')), 'div.B.C.D');
assertElementsMatch(list.getElementsByClassName('B'));
assertElementsMatch(list.getElementsByClassName('1'), 'li.1');
assertElementsMatch(list.getElementsByClassName([1]), 'li.1');
assertElementsMatch(list.getElementsByClassName(['1 junk']));
assertElementsMatch(list.getElementsByClassName(''));
assertElementsMatch(list.getElementsByClassName(' '));
assertElementsMatch(list.getElementsByClassName(['']));
assertElementsMatch(list.getElementsByClassName([' ', '']));
assertElementsMatch(list.getElementsByClassName({}));
assertEqual(Array, [].constructor)
// those lookups shouldn't have extended all nodes in document
if (Prototype.Browser.IE) assertUndefined(document.getElementById('unextended')['show']);
}},
testElementInsertWithHTML: function() {with(this) {