className and IE

Hallo,
I’ve got a set of code ( of which I only understand half of it as I was only able to write half of it… someone else wrote the rest as I am hitting deadlines) which does almost nothing in IE (also not in IE8) while working pretty well in all other browsers (safari, konq, ff, opera, chrome). I’m still in the process of fully understanding this code while also trying to fix it, which isn’t necessarily a good combination… but it’s how it is for now.

Here is a temp link to the page, the JS is inside right after the part it’s affecting.

IE gives the error:
Object Required
Line 290, char 9

That’s all it says. I know IE isn’t known for reporting the right line but I think it’s probably right here. This is the JS in its entirety and I marked line 290:


    <script type="text/javascript">
    function hasClass(target, classValue) {
        var pattern = new RegExp('(^| )' + classValue + '( |$)');
/*this is line 290*/        if (pattern.test(target.className)) {
            return true;
        }
        return false;
    }
    
    function addClass(target, classValue) {
        if(!hasClass(target, classValue)) {
            if (!target.className == '') {
                classValue = ' ' + classValue;
            }
            target.setAttribute('class', target.className + classValue);
        }
    }

    function removeClass(target, classValue) {
        if(!hasClass(target,classValue)) {
          return;
        }
        var removedClass = target.className;
        var pattern = new RegExp('(^| )' + classValue  + '( |$)');
        removedClass = removedClass.replace(pattern, '$1');
        removedClass = removedClass.replace(/ $/, '');
        target.setAttribute('class',removedClass);
    }

    function createAnchor(text, href) {
        var anchor = document.createElement('a');
        anchor.appendChild(document.createTextNode(text));
        anchor.href = '#' + href;
        return anchor;
    }

    function toggle(target,classValue) {
        if (hasClass(target,classValue)) { 
            removeClass(target, classValue);
        } else {
	        addClass(target, classValue);
	    }
    }

var toggleFunc = function() {
    var prN = this.parentNode;
    toggle(prN.nextSibling.nextSibling,'verberg');

    var signNode = this.firstChild;
    signNode.nodeValue = signNode.nodeValue == '+' ? '-' : '+';

    if(prN.firstChild.nodeValue === 'Algemeen') {
        var kids = prN.parentNode.getElementsByTagName('ul');
        for(var i = 0, j = kids.length; i<j; i++) {
            var prevSib = kids.item(i).previousSibling.previousSibling;

            anchorNode = prevSib.firstChild.nextSibling.firstChild;            
            anchorNode.nodeValue = signNode.nodeValue == '-' ? '-' : '+';
            
            if(signNode.nodeValue == '-') {
                removeClass(kids.item(i),'verberg');    
            } else {
                addClass(kids.item(i),'verberg');
            } 
        }
    } 
    return false;
};

var bLinks = document.getElementById('beschrijflinks');
var divs = bLinks.getElementsByTagName('div'), 
    h2s  = bLinks.getElementsByTagName('h2');
    
for( var i = 0, j = h2s.length; i < j; i++) {

    var div = divs.item(i);

    var h3s = div.getElementsByTagName('h3');
    for( var k = 0, l = h3s.length; k < l; k++) {
        var anchor = createAnchor('+', 'a_'+k);
        h3s.item(k).appendChild(anchor);
        anchor.onclick = toggleFunc;
        toggle(h3s.item(k).nextSibling.nextSibling,'verberg');
    }

    var anchor = createAnchor('+', 'a_'+i);
    h2s.item(i).appendChild(anchor);
    anchor.onclick = toggleFunc;
            
    toggle(div,'verberg');    
}
    
    </script>

With this line
if (pattern.test(target.className)) {
left unchanged, IE tries to implement the adding of anchors to the first set of anchors, but doesn’t do anything else (though looking in the IE dev toolbar it claims the class of “verberg” (hidden) is added to the elements on page load… but if they really were, the CSS would have kicked in and hidden those elements).
If I change (just to check) className to target.class then I have no error messages in IE and nothing works at all.

I think every place where className is used, IE is not getting it at all. I suspect this line:
target.setAttribute(‘class’, target.className + classValue);
(line 301 in the addClass function) and I wonder if I have to use something like
target.setAttribute(
(‘class’ ? ‘class’ : className),
target.className + classValue);
or something? I’m not sure how to write an IE-version of every line who needs it. I’m not sure how to deal with it. Should I try instead writing two separate lines, one target.setAttribute for modern browsers and another separate line for IE?

Ideally also if there’s a site somewhere someone knows of who specifically deals with writing JS for everyone and specifically dealing with IE and different methods of object detection (with syntax examples) that would be very nice.

Debugging on IE the problem is that target is null. The call stack shows

toggle(h3s.item(k).nextSibling.nextSibling,‘verberg’);

as being the place where target gets supplied. That is h3s.item(k).nextSibling.nextSibling is null

Haven’t got to grips with the logic yet but hope this might help.

I had found a couple of forums where people running into similar trouble found that parentNodes of things were different in IE.

I wonder if it’s not counting whitespace as nodes?
Maybe I will try adding the
while (blah.nodeType == 3) {
//move on to the nextSibling;
}
and remove one of the nextSibling’s in there.

Yes, that will definately be the problem.

Internet Explorer is unique (hah!) in that it doesn’t incluse whitespace elements when using moving from one sibling to the next.

It may help if you use a separate function to move to the next sibling, where you can perform the appropriate checks:


function nextSibling(el) {
    el = el.nextSibling;
    while (el && el.nodeType !== 3) {
        el = el.nextSibling;
    }
    return el;
}

}

Hm, neat. Better than adding a while statement in every chunk that looks for the nextSibling.

I’ve got to try out one thing at a time though. I’ve got the setAttribute “class”/className problem and the nextSibling problem. I’m going to try the next sibling one first.

All the best with it.

Regarding the class names, we haven’t gone wrong by using the functions from this snippet
http://snipplr.com/view/3561/addclass-removeclass-hasclass/


function hasClass(ele,cls) {
	return ele.className.match(new RegExp('(\\\\s|^)'+cls+'(\\\\s|$)'));
}
 
function addClass(ele,cls) {
	if (!this.hasClass(ele,cls)) ele.className += " "+cls;
}
 
function removeClass(ele,cls) {
	if (hasClass(ele,cls)) {
    	var reg = new RegExp('(\\\\s|^)'+cls+'(\\\\s|$)');
		ele.className=ele.className.replace(reg,' ');
	}
}

They’re so good that they’re close to being best-practice material.

My husband had initially something very similar to that addClass posted above, whereas my original code had the addClass function from Javascript Anthology, as I read that Opera was not happy with
class=" classname" with the leading space.
I had him keep the check for other classes first, if none add the classname, otherwise add the space = classname…
if this was only a very old Opera bug then I’d be happy to have leaner code!

Is is safer to use \\s in the regex than a regular space? I understand that \s can also take care of tabs, but is that ever an issue? Just curious.

If you believe that it will cause a problem, you can very easily resolve that with this update that applies the space only if there is already something there:


if (!this.hasClass(ele,cls)) {
    if (ele.className &gt; '') {
        ele.className += ' ';
    }
    ele.className += cls;
}


[quote="Stomme_poes,post:7,topic:47325"]
Is is safer to use \\\\s in the regex than a regular space?  I understand that \\s can also take care of tabs, but is that ever an issue?  Just curious.
[/quote]


\\\\s is the best one to use as it neatly resolves edge-case issues without introducing further difficulties.

lijst[l].setAttribute(“class”, “verberg”);
lijst[l].className = “verberg”;
If I could just do that then why am I not just using the second line in the first place?
Precisely :tup: I wasn’t suggesting you use both. Avoid setAttribute getAttribute for ‘class’ and ‘for’ attributes.

Precisely I wasn’t suggesting you use both. Avoid setAttribute getAttribute for ‘class’ and ‘for’ attributes.

Ah, ok, but I couldn’t get the addClass/removeClass functions (as they came out of the box from the Anthology book) to work at all (back with original code) until at least once the setAttribute was used… so I figured it was necessary to use setAttribute.

A problem for me with code snippets is they don’t have much context I can look at and copy.