Safari and getElementsByClassName

With the advent of Safari 3.1 and the proclaimed support for new DOM methods (among which is getElementsByTagName) comes of course the let-down.

It would appear that getElementsByClassName is broken in Safari. It works great the first time but unfortunately its internal DOM registry don't get updated, so if one changes the class name that one has searched for, it is still registered and will be returned the next time the query is performed.

 Consider the following HTML code:

<ul id="animals">
<li class="active">cat</li>
<li>dog</li>
<li>mouse</li>
</ul>

With the following Javascript:

function switchActive( x )
{
document.getElementById( 'animals' ).getElementsByClassName( 'active' )[0].removeAttribute( 'active' );
document.getElementById( 'animals' ).getElementsByTagName( 'li' )[x].setAttribute( 'class', 'active' );
}
switchActive( 1 );
switchActive( 2 );

This will break in Safari because the browser still thinks that the first LI element has the class active applied to it, so it never removes that class from the second LI element.

The work-around is to force Safari to refresh its DOM registry, perhaps by using cloneNode on the LI element from which the class is being removed.

Of course, the really annoying problem is that with libraries and scripts that try to implement a working getElementByClassName method by extending the Element object will now break as they will use Safari's native implementation (unless of course they update the checks to browser sniff for Safari). All round, this is a very annoying bug!

Comments

Number: 01

Date: 02.02.09

Lawrence said:

There is a much better work around. getElementsByClassName is not broken, as such, but has a preformance feature for which your did not account.

function switchActive( x )

{

var parentEl = document.getElementById( 'animals' );

var activeList = getElementsByClassName( 'active' );

for (var i = 0, j = activeList.length; i < j; i++) {

if (activeList[i]) removeAttribute( 'active' );

}

parentEl.getElementsByTagName( 'li' )[x].setAttribute( 'class', 'active' );

}

This issue is that Safari caches the list of elements that matched based on the exact class name (a trailing space makes a difference). The next time the same class name is sought it uses the previous "array" setting to null any array elements where the HTML element no longer has the class in question and adding any new elements the now have the class.

I'm sure this increases the performance of subsequent calls to getElementByClassName; although, it would have been better to remove the "array" element.

But, the "fix" is simply to check that the "array" element is not null before working with it.

Number: 02

Date: 02.02.09

Khaitu replied:

Fascinating. Thanks for that insight! I haven't run into this problem since though as the use of libraries such as jQuery make getElementsByClassName unnecessary and actually long-winded. In any case, if one were to snub libraries, the querySelectorAll method would be more efficient. I do wonder however whether querySelectorAll caches elements as well.

Number: 03

Date: 07.07.12

payday loans said:

sclqibof http://paydayloanslci.com/ payday loans >:]] http://paydayloanslci.ca/ Cash Advance >:]] http://paydayloanslci.co.uk/ payday loans %-[[[

Number: 04

Date: 09.07.12

Payday UK said:

botxbs http://paydaylaonsffi.co.uk/ Payday UK 0951 http://paydaylaonsffi.com/ payday loan hMTDx

Number: 05

Date: 12.07.12

Bobbo said:

Number: 06

Date: 18.07.12

payday loans said:

ooocakgc http://paydayloansvzh.co.uk/ payday loans %-[[[ http://paydayloansvzh.com/ instant payday loans axwswd http://paydayloansvzh.ca/ payday loans online >:]]

Post a comment

Comment details
anti-spam code. you need to be able to view images to post a comment. sorry.