setAttribute doesn't work in IE6 - javascript

how to set element attribute with javascript in IE6..? It seems setAttribute doesn't work. I really need to do it on the fly. Thanks.
Code
<script type="text/javascript" language="javascript">
menuItems = document.getElementById("menu").childNodes;
for (i = 0; i < menuItems.length; i++)
{
if (menuItems[i].className != "blue")
menuItems[i].setAttribute('onmouseover', 'HoverMenu(this)');
}
</script>

(Most of the below was before the OP clarified they were setting an event handler; left the list of other issues in case others find them useful)
IE6 makes a mess of several aspects of setAttribute. Without knowing the exact problem you were dealing with (this was before the edit inidicating it was an event handler), it's hard to be sure whether that's really the problem, but here are a couple of known issues:
You can't use setAttribute to set event handlers
It's best to set event handlers using the reflected property or with addEventListener [standard] / attachEvent [IE], not setAttribute (and you can't use setAttribute on IE).
So, any of these will work:
// Using reflected property
theElement.onclick = yourFunction;
// Using DOM2 standard addEventListener; note it's "click", not "onclick"
theElement.addEventListener("click", yourFunction, false);
// IE's non-standard alternative to addEventListener
theElement.attachEvent("onclick", yourFunction);
...not
// This doesn't work on IE (at least)
theElement.setAttribute("onclick", "yourFunction();");
The addEventListener / attachEvent way of doing this is cool for other reasons: You can have multiple event listeners on the same event of an element. You can't do that using the reflected property.
So for your specific situation:
menuItems = document.getElementById("menu").childNodes;
for (i = 0; i < menuItems.length; i++)
{
if (menuItems[i].className != "blue") {
menuItems[i].onmouseover = function() {
HoverMenu(this);
}
}
}
Certain attributes need their modified names
class
The correct way to set the class attribute is to use the reflected property className:
// Correct, cross-browser way. Works on IE6+, all versions of
// Chrome, etc. Completely reliable.
theElement.className = "new set of classes";
or on modern browsers (so, not IE6!) you can use classList.
One of IE6's many setAttribute bugs is that this doesn't work:
// Should also work, but fails on IE6 (and probably some other old versions)
theElement.setAttribute("class", "new set of classes");
Instead, IE6 (and probably a couple of other versions) make you use "className" instead of "class", even though that makes no sense whatsoever. The reflected property has the name className because it used to be that in JavaScript, you couldn't use reserved words (like class or for or if) as property literals (obj.class was invalid). That's not been true for a while now (ECMAScript 5 fixed it), but that's why the reflected property is className, not class.
But since setAttribute takes a string, it should accept the proper name of the attribute. The fact it doesn't is just an IE bug (and one they've fixed in modern versions of IE if they're not in [in]compatibility mode).
for
Similarly, to set the for attribute (for instance, on labels), one uses the htmlFor reflected property:
// Correct, cross-browser way. Works on IE6+, all versions of
// Chrome, etc. Completely reliable.
theLabel.htmlFor = "id-of-field";
On any non-broken browser, you can also use setAttribute:
// Should also work, but fails on IE6 (and probably some other old versions)
theLabel.setAttribute("for", "id-of-field");
...but not on IE6, it wants "htmlFor" instead of "for" for the same reason it wants "className" rather than "class" (e.g, because it's broken).
This page has quite a list of attributes that are problematic with IE.
setAttribute can't be used to set the style attribute
...you have to use the style property instead; but to be fair, that's usually a more convenient way. Example: This won't work on IE:
theElement.setAttribute("style", "color: blue"); // Doesn't work on IE
...but this will:
myElement.style.color = "blue";
Slightly OT: Look at libraries
JavaScript libraries like Prototype, jQuery, Closure, or any of several others will make most of the above a lot easier and smooth out differences amongst browsers if you go through their APIs.

I would really look at jquery. It has all the functionality that works with IE6 and this would be so much easier than the code you have here. It would look like this:
menuItems = $("#menu")[0].childNodes;
$.each(menuItems, function()
{
var item = $(this);
if (item.attr("className") != "blue")
item.mouseover(HoverMenu);
}
This code might need to be tweaked a little as I am just writing from memory.
I say easier because what you are trying to do in setting events like this varies based on browser and can be a headache to setup. But with jquery it is all done for you.

Related

Access 'data-' attribute without jQuery

Can I access a data- attribute without jQuery?
It's easy with jQuery, but I can't see anywhere how to do it without jQuery.
If I search on Google 'without jQuery' all I get is jQuery examples.
Is it even possible?
On here I found this example:
<div id='strawberry-plant' data-fruit='12'></div>
<script>
// 'Getting' data-attributes using getAttribute
var plant = document.getElementById('strawberry-plant');
var fruitCount = plant.getAttribute('data-fruit'); // fruitCount = '12'
// 'Setting' data-attributes using setAttribute
plant.setAttribute('data-fruit', '7'); // Pesky birds
</script>
So it would appear very doable.
Update: Since Microsoft is now (2020) phasing out the old Internet Explorer engine in favour of a Chromium based Edge, the dataset property is likely to work everywhere. The exception will, for a time, be organizations and corporate networks where IE is still forced. At the time of writing this though - jsPerf still shows the get/setAttribute method as being faster than using dataset, at least on Chrome 81.
You could use the dataset attribute. As in:
element = document.getElementById("el");
alert(element.dataset.name); // element.dataset.name == data-name
It's just an attribute ... use getAttribute as with any other attribute : https://developer.mozilla.org/en/docs/DOM/element.getAttribute
Or am I missing the point of your question.
You can also use:
getAttribute('data-property');
Which is a bit cleaner and easier to read.
This will get the value of the data attribute.
I think you can try this:
var ele = document.getElementById("myelement");
if (ele.hasOwnProperty("data")) {
alert(ele.data);
}
OR use
alert(ele['data-property']);

Clear element.classList

element.classList is of DOMTokenList type.
Is there a method to clear this list?
I'm not aware of a "method" in the sense of a "function" associated with classList. It has .add() and .remove() methods to add or remove individual classes, but you can clear the list in the sense of removing all classes from the element like this:
element.className = "";
With ES6 and the spread operator, this is a breeze.
element.classList.remove(...element.classList);
This will spread the list items as arguments to the remove method.
Since the classList.remove method can take many arguments, they all are removed and the classList is cleared.
Even though it is readable it is not very efficient. #Fredrik Macrobond's answer is faster.
View different solutions and their test results at jsperf.
var classList = element.classList;
while (classList.length > 0) {
classList.remove(classList.item(0));
}
Here's another way to do it:
element.setAttribute("class", "")
Nowadays, classList is preferred to (remove|set)Attribute or className.
Pekaaw's answer above is good, 1 similar alternative is to set the DomTokenList.value
elem.classList.value = ''
Another option is to simply remove the class attribute:
elem.removeAttribute('class')
I recommend not using className as classList could result in faster DOM updates.
The remove() method of DOMTokenList (which is what classList is) can take multiple arguments - each a string of a classname to remove (reference). First you need to convert the classList to a plan array of classnames. Some people use Array.prototype.slice() to do that, but I'm not a fan and I think a for loop is faster in most browsers - but either way I have nothing against for loops and the slice often feels like a hack to me. Once you have the array you simply use apply() to pass that array as a list of arguments.
I use a utility class I wrote to accomplish this. The first method is the one you are looking for, but I'm including slightly more for your reference.
ElementTools.clearClassList = function(elem) {
var classList = elem.classList;
var classListAsArray = new Array(classList.length);
for (var i = 0, len = classList.length; i < len; i++) {
classListAsArray[i] = classList[i];
}
ElementTools.removeClassList(elem, classListAsArray);
}
ElementTools.removeClassList = function(elem, classArray) {
var classList = elem.classList;
classList.remove.apply(classList, classArray);
};
ElementTools.addClassList = function(elem, newClassArray) {
var classList = elem.classList;
classList.add.apply(classList, newClassArray);
};
ElementTools.setClassList = function(elem, newClassArray) {
ElementTools.clearClassList(elem);
ElementTools.addClassList(elem, newClassArray);
};
Please note that I have not thoroughly tested this in all browsers as the project I am working on only needs to work in a very limited set of modern browsers. But it should work back to IE10, and if you include a shim (https://github.com/eligrey/classList.js) for classList, it should work back to IE7 (and also with SVGs since Eli Grey's shim adds support for SVG in unsupported browsers too).
An alternative approach I considered was to loop backwards through the classList and call remove() on classList for each entry. (Backwards because the length changes as you remove each.) While this should also work, I figured using the multiple arguments on remove() could be faster since modern browsers may optimize for it and avoid multiple updates to the DOM each time I call remove() in a for loop. Additionally both approaches require a for loop (either to build a list or to remove each) so I saw no benefits to this alternative approach. But again, I did not do any speed tests.
If somebody tests speeds of the various approaches or has a better solution, please post.
EDIT: I found a bug in the shim which stops it from correctly adding support to IE11/10 for multiple arguments to add() and remove(). I have filed a report on github.

Removing Chrome's "translate" DOM Property

I'm working with some legacy code where the original developers made heavy use of generating HTML DOM nodes with a non-standard attribute named translate
<span translate="[{"shown":"My Account","translated":"My Account","original":"My Account","location":"Text","scope":"Mage_Customer"}]">My Account</span>
and then traversing/searching for those nodes with javascript code like the following.
if (!$(target).match('*[translate]')) {
target = target.up('*[translate]');
}
The problem I'm trying to solve is, it appears that Google Chrome automatically adds a translate attribute to every DOM node in the document, and that this DOM node's value is a boolean true. You can see this by running the following Javascript from Chrome's javascript console
> document.getElementsByTagName('p')[0].translate
true
>
Is there anyway to tell Chrome not to populate these attributes? Their presence is wrying havoc with the legacy code. PrototypeJS's match and up nodes treat these boolean object attributes as matches, while the code I'm dealing with is specifically looking for DOM nodes with an attribute named translate. I'd like to find a solution for my problem that doesn't involved rewriting the old Javascript to use methods like hasAttribute.
I tried (as a wild guess) adding the meta attributes mentioned in this article,
<meta name=”google” value=”notranslate”>
<meta name=”google” content=”notranslate”>
but the nodes in the page still has a boolean true translate attribute.
(if it matters, this is Magento's inline translation system I'm talking about here)
The best I've been able to come up with so far is going through every DOM element in the page defining a getter that checks for the existence of an attribute. (the Object.__defineGetter__ guard clause ensures no errors in browsers that don't support modern Javascript)
if(Object.__defineGetter__)
{
var hasTranslateAttribute = function(){
return $(this).hasAttribute("translate");
};
document.observe("dom:loaded", function() {
$$('*').each(function(theElement){
theElement.__defineGetter__("translate", hasTranslateAttribute);
});
});
}
I tried defining a getting on Object.prototype and Element.prototype, but it seems like the browser's native translate is defined higher up the chain, so you need to redefine things on a per element basis.
Replace the nonstandard attribute translate by an attribute like data-translate, which is virtually guaranteed to be and to remain undefined in HTML specifications and in browsers. The data-* attributes were invented to prevent issues like this, and they can also be used to fix them.

Setting a property via property or setAttribute

Is one of these more preferable than the other? Why? How about performance--if these are being called thousands of times?
A) element.setAttribute("disabled", true);
B) element.disabled = true;
They both seem to disable an input[text] element in FF 4.
In general…
Use properties. For a long time (until version 7 or 8 IIRC) Internet Explorer had a seriously broken implementation of setAttribute that would set the property not the attribute (the classic point of failure was class since there is no class property (it is className).
In this case in particular… element.setAttribute("disabled", true); is wrong. It should be element.setAttribute("disabled", "disabled");
element.setAttribute("disabled", some_bool) doesn't work like you'd think it will. In particular, standardswise, disabled is what's known as a boolean attribute; its very presence, regardless of its value, makes it true. disabled="", disabled="disabled", disabled="true" and even disabled="false"(!!!) all mean the same thing in most browsers. (Although the last two are actually invalid HTML, most browsers will consider them equivalent to disabled="disabled" for truth purposes. Including every one of the Big Four.) You set a boolean attribute to true by setting a value -- any value, even if it's falsy -- and you set it to false by removing the attribute entirely.
If you care about the actual string value of the attribute (which in this case you shouldn't), and particularly if the attribute isn't already exposed via the DOM (that is, it doesn't have a corresponding property), then use (get/set)Attribute. In most cases (particularly if you care about how it affects the element, like in this case where you're trying to disable an element), use the DOM property.
IE needs some attributes to be set with setAttribute, but not all. I don't have a list though, you just have to check if it works or not. Also, using setAttribute will put the attribute in the DOM, so it can be shown when doing view source.
Only one tip: element.setAttribute('class',xxx) doesnt works in some versions of IE.
Prefer element.className = xxx instead

How to add/update an attribute to an HTML element using JavaScript?

I'm trying to find a way that will add / update attribute using JavaScript. I know I can do it with setAttribute() function but that doesn't work in IE.
You can read here about the behaviour of attributes in many different browsers, including IE.
element.setAttribute() should do the trick, even in IE. Did you try it? If it doesn't work, then maybe
element.attributeName = 'value' might work.
What seems easy is actually tricky if you want to be completely compatible.
var e = document.createElement('div');
Let's say you have an id of 'div1' to add.
e['id'] = 'div1';
e.id = 'div1';
e.attributes['id'] = 'div1';
e.createAttribute('id','div1')
These will all work except the last in IE 5.5 (which is ancient history at this point but still is XP's default with no updates).
But there are contingencies, of course.
Will not work in IE prior to 8:e.attributes['style']
Will not error but won't actually set the class, it must be className:e['class'] .
However, if you're using attributes then this WILL work:e.attributes['class']
In summary, think of attributes as literal and object-oriented.
In literal, you just want it to spit out x='y' and not think about it. This is what attributes, setAttribute, createAttribute is for (except for IE's style exception). But because these are really objects things can get confused.
Since you are going to the trouble of properly creating a DOM element instead of jQuery innerHTML slop, I would treat it like one and stick with the e.className = 'fooClass' and e.id = 'fooID'. This is a design preference, but in this instance trying to treat is as anything other than an object works against you.
It will never backfire on you like the other methods might, just be aware of class being className and style being an object so it's style.width not style="width:50px". Also remember tagName but this is already set by createElement so you shouldn't need to worry about it.
This was longer than I wanted, but CSS manipulation in JS is tricky business.
Obligatory jQuery solution. Finds and sets the title attribute to foo. Note this selects a single element since I'm doing it by id, but you could easily set the same attribute on a collection by changing the selector.
$('#element').attr( 'title', 'foo' );
What do you want to do with the attribute? Is it an html attribute or something of your own?
Most of the time you can simply address it as a property: want to set a title on an element? element.title = "foo" will do it.
For your own custom JS attributes the DOM is naturally extensible (aka expando=true), the simple upshot of which is that you can do element.myCustomFlag = foo and subsequently read it without issue.

Categories

Resources