Site Navigation

Showing posts with label getElementById. Show all posts
Showing posts with label getElementById. Show all posts

Wednesday, August 5, 2009

bug 409 - MooTools redundant getElementById(id) method

Issue: #409
Affects: MooTools

If you are developing any web sites or web applications these days you are undoubtedly using a JavaScript Framework or have plans to implement one soon.


This bug is about the MooTools framework, specifically about the Element.getElementById() method.

The docs indicate:
Synopsis: Gets the element with the specified id found inside the current Element.
Example: var myChild = $('myParent').getElementById('myChild');
Notes: This method is not provided for Document instances as document.getElementById is provided natively.


Did you spot it? No? Lets run through it bit by bit.

For starters MooTools uses a very simple $('someID') syntax to get an element by its ID.

Combine that with the fact that the ID attribute of HTML elements must be unique within the document according to the W3C specs... and you've now proven that the .getElementById() method on any other element in the DOM is redundant.

E.g. the above example can also be written as (and in much less code I might add):
Example: var myChild = $('myChild');

Now maybe I'm missing something here that is hidden in the docs that helps explain why verbose, redundant code is somehow helpful or that it provides additional features? If so, please comment and let me know!



Known Workarounds: One. Simply don't use it! Its too bad that the API is cluttered with this extra method but it won't cause any harm to use it.

Example Workaround Code:

var myChild = $('myChild');



When you consider that your JavaScript Framework is providing an API on top of the existing JavaScript/ECMAScript API... you want your Framework API to be as simple and lightweight as possible. Its a minor bug but one that should be addressed to maintain a clean and tight API.

Related Issues: None.

Bug/Site Feedback |
Submit a bug

Tuesday, September 25, 2007

bug 154 - getElementById is NOT case sensitive in IE

Issue: #154
Affects: IE6, IE7
Fixed In: IE8

MSIE Feedback ID: 333979

"Just when you thought it was safe to go back in the water"! (credit to JAWS the movie) IE's getElementById( id ) throws another curve ball!

Although it should be according to the (spec), IE performs a case-insensitive search for IDs. This isn't likely as bad as (bug 152), but still a concern, if you have 2 IDs with different cases.

Example:

<form id="userInfo">
<input id="userid" value="grovermonster"/>
<input id="userfirst" value="Grover"/>
<input id="userlast" value="Monster"/>
<textarea id="userinfo">
Cute and cuddly and Blue!
</textarea>
<form>
<script type="text/javascript">
var infoForUser = document.getElementById('userinfo').value;
alert('Info for this user:\n\n' + infoForUser);
</script>


In IE, this won't return the "Cute and cuddly and Blue!" line you would expect. Instead, IE tries to return the value of the form itself.


Known Workarounds: One. Use the fix for (bug 152)


Related Issues: (bug 152).

bug 162 - global namespace pollution in IE

Issue: #162
Affects: IE5, IE5.5, IE6, IE7

Every element on a page in IE with an ID or a NAME creates (cough, pollutes!) the global JavaScript namespace. Not only is this bad practice, but it is highly prone to errors down the road.

Example:

<div id="myfoo"></div>
<div id="mybar"></div>
.
.
.
<script type="text/javascript">
myfooStr = 'Hello World';
myfoo = document.getElementById('myfoo');
myfoo.innerHTML = myfooStr;
</script>


In IE, the above code will cause errors... in any other browser it will simply set the content of the div to "Hello World". Why an error? Well since IE creates a global variable in the JavaScript namespace for each element on the page, with an ID, before the script even executes, myfoo is already defined. In the script, attempting to reference it to something (even in this case the same element), IE throws a script error.


Known Workarounds: One. Watch the scope of variables. (e.g. use 'var', inside functions to keep the scope local)

Example Workaround Code:

<div id="myfoo"></div>
<div id="mybar"></div>
.
.
.
<script type="text/javascript">
function doStuff(){
var myfooStr = 'Hello World';
var myfoo = document.getElementById('myfoo');
myfoo.innerHTML = myfooStr;
}
</script>



Related Issues: None.

Friday, August 24, 2007

bug 152 - getElementById returns incorrect objects in IE and Opera

Issue: #152
Affects: IE5, IE5.5, IE6, IE7, Opera 8.2, Opera 9.2
Fixed in: Opera 9.50 alpha 1 build 9500
Almost Fixed in: IE8 Beta 1
Fixed in: IE8 Beta 2

MSIE Feedback ID: 333979

Example:
<script type="text/javascript">
var descField = document.getElementById( 'description' );
alert( descField.nodeName );
</script>

The above code works perfectly, in all browsers returning the element with the id "description". Well almost. IE will return the element with that id, but it will also return any element with a name set to "description". At first glance this may not seem such an issue, but consider this; Do you have a meta tag on any of your pages? Do any of them have a name attribute with the value description? If so, you will get a reference to the meta tag, not the form element (or whatever you thought you were getting, as indicated in the spec for getElementById).

When you think for a moment, about where the name attribute is set, this becomes rather scary. Any named anchor, can now conflict with your well defined element ids.

Notes:
So you might ask, why is this broken in Opera? Opera is usually pretty good at supporting the specs! Well, it seems that for maximum compatibility with IE, even though it is implemented wrong, they mimicked the broken behavior.


Known Workarounds: No direct methods, using Option #3 below is suggested.

Workaround Option: 1
If you rely heavily on getElementById, and you suspect that you may have name conflicts, or that user specific content added to a page may cause conflicts you can use getElementsByTagName( tagName ) then iterate over the results and compare the value returned by getAttribute( 'id' ).

Example Workaround Code:
var inputs = document.getElementsByTagName( 'textarea' );
var descField = null;
for(var i=0;i<inputs.length;i++){
if(inputs.item(i).getAttribute( 'id' ) == 'description' ){
descField = inputs.item(i);
break;
}
}




Workaround Option: 2
If you have the ability to globally apply fixes for IE and Opera, the following code will make both of them follow the spec to a 'T', with a very tiny performance hit.

In this example, we redifine the getElementById method, to work as it was intended.
Example Workaround Code:
//use browser sniffing to determine if IE or Opera (ugly, but required)
var isOpera, isIE = false;
if(typeof(window.opera) != 'undefined'){isOpera = true;}
if(!isOpera && navigator.userAgent.indexOf('Internet Explorer')){isIE = true;}

//fix both IE and Opera (adjust when they implement this method properly)
if(isOpera || isIE){
document.nativeGetElementById = document.getElementById;
//redefine it!
document.getElementById = function(id){
var elem = document.nativeGetElementById(id);
if(elem){
//verify it is a valid match!
if(elem.id == id){
//valid match!
return elem;
} else {
//not a valid match!
//the non-standard, document.all array has keys for all name'd, and id'd elements
//start at one, because we know the first match, is wrong!
for(var i=1;i<document.all[id].length;i++){
if(document.all[id][i].id == id){
return document.all[id][i];
}
}
}
}
return null;
};
}



Oh my! this just gets better and better! (after posting and using this I noted (as did J. Max Wilson) that the second workaround, actually exposes another bug in IE!

Third time's a charm (we hope!)

Workaround Option: 3

Same as workaround 2 above, but rather than test elem.id or elem.getAttribute('id') we'll use the much safer elem.attributes collection. Unfortunately if the element you are looking for, in turn has a child element with a name or id attribute, that is set to "id", it will not test the attribute, but rather the child element. (bug 162 - global namespace pollution)

Example Workaround Code:
//use browser sniffing to determine if IE or Opera (ugly, but required)
var isOpera, isIE = false;
if(typeof(window.opera) != 'undefined'){isOpera = true;}
if(!isOpera && navigator.userAgent.indexOf('Internet Explorer')){isIE = true;}

//fix both IE and Opera (adjust when they implement this method properly)
if(isOpera || isIE){
document.nativeGetElementById = document.getElementById;
//redefine it!
document.getElementById = function(id){
var elem = document.nativeGetElementById(id);
if(elem){
//verify it is a valid match!
if(elem.attributes['id'] && elem.attributes['id'].value == id){
//valid match!
return elem;
} else {
//not a valid match!
//the non-standard, document.all array has keys for all name'd, and id'd elements
//start at one, because we know the first match, is wrong!
for(var i=1;i<document.all[id].length;i++){
if(document.all[id][i].attributes['id'] && document.all[id][i].attributes['id'].value == id){
return document.all[id][i];
}
}
}
}
return null;
};
}




Related Issues: (bug 154), (bug 411), (bug 162).