Setting a property via property or setAttribute - javascript

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

Related

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.

setAttribute doesn't work in IE6

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.

Why doesn't IE7 think these two strings are equal?

I have an event handler that will remove an element from a list of the corresponding checkbox is unchecked. In the handler for the click event for the checkbox, first I copy the value of the label for that checkbox:
var label = $(this).next().html();
Then, I iterate over the list items and compare each of them to that label:
$("#sortable li").each(function() {
if ($(this).html() === label) {
$(this).remove();
}
});
In Internet Explorer 8 and in Firefox, this works exactly as I'd expect. In Internet Explorer 7, it does not. The condition in the "if" statement is never true. What am I missing here?
By request, the strings being compared are, "Charge" and "Charge" in one case.
Try alert(label) and alert($(this).html()) to see exactly what's being compared.
I'm not seeing any reason to use strict comparison (===), and you could conceivably benefit from using ordinary comparison (==).
As a method of doing what you're saying you want to do, this all seems pretty crazy. I'd always recommend identifying corresponding DOM elements by something more designed-for-the-purpose than their HTML contents, such as an ID.
Why don't you use the Script Debugger and see exactly why that comparison is false? Just set a breakpoint in the if and watch the variables.
The IE8 internal script debugger will help you, just switch to compatibility mode to run the script in the IE7 javascript runtime
Have you tried debugging it? Use alert to see what the two values hold, check that the function is actually called in IE7 and check that they have the same type.
Not an answer to your direct question, but here's another method that uses jQuery's data method to set and read a flag directly on the element:
$(this).next().data("toRemove", true);
//...
$("#sortable li").each(function() {
if ($(this).data("toRemove")) {
$(this).remove();
}
});
Of course, if you were manipulating label at any point then this wouldn't be what you want. I just mention this because .data() is a very useful jQuery feature that I haven't heard much about. I didn't even know about it until a week ago.

How can I get the font size of an element as it was set by css

I'm writing a simple jQuery that to change the font size of an element by a certain percentage. The problem I'm having is that when I get the size with jQuery's $('#el').css('font-size') it always returns a pixel value, even when set with em's. When I use Javscript's el.style.font-size property, it won't return a value until one has been explicitly set by the same property.
Is there some way I can get the original CSS set font-size value with Javascript? How cross-browser friendly is your method?
Thanks in advance!
Edit: I should note that all the tested browsers (See comment below) allow me to set the the text-size using an 'em' value using the two methods mentioned above, at which point the jQuery .css() returns an equivalent 'px' value and the Javascript .style.fontSize method returns the correct 'em' value. Perhaps the best way to do this would be to initialize the elements on page load, giving them an em value right away.
All of your target browsers with the exception of IE will report to you the "Computed Style" of the element. In your case you don't want to know what the computed px size is for font-size, but rather you want the value set in your stylesheet(s).
Only IE can get this right with its currentStyle feature. Unfortunately jQuery in this case works against you and even gets IE to report the computed size in px (it uses this hack by Dean Edwards to do so, you can check the source yourself).
So in a nutshell, what you want is impossible cross-browser. Only IE can do it (provided you bypass jQuery to access the property). Your solution of setting the value inline (as opposed to in a stylesheet) is the best you can do, as then the browser does not need to compute anything.
In Chrome:
var rules = window.getMatchedCSSRules(document.getElementById('target'))
returns a CSSRuleList object. Need to do a bit more experimentation with this, but it looks like if m < n then the CSSStyleRule at rules[n] overrides the one at rules[m]. So:
for(var i = rules.length - 1; i >= 0; --i) {
if(rules[i].style.fontSize) {
return /.*(em|ex|ch|rem|vh|vw|vmin|vmax|px|in|mm|cm|pt|pc|%)$/.exec(rules[i].style.fontSize)[1];
}
}
In Firefox, maybe use sheets = document.styleSheets to get all your stylesheets as a StyleSheetList, then iterate over each CSSStyleSheet sheet in sheets. Iterate through the CSSStyleRules in sheet (ignoring the CSSImportRules) and test each rule against the target element via document.getElementById('target').querySelector(rule.selectorText). Then apply the same regexp as above..... but that's all just a guess, I haven't tested it out or anything....
This jQuery plugin looks like it will do the trick:
Update: jQuery Plugin for Retaining Scalable Interfaces with Pixel-to-Em Conversion
Github: jQuery-Pixel-Em-Converter
i had this problem once. i use this function to get the int value of a css attribute, if there is one.
function PBMtoInt(str)
{
return parseInt(str.replace(/([^0-9\.\-])+/,"")!=""?str.replace(/([^0-9\.\-])+/,""):"0");
}

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