#156 enhancement
Ricky

Fixed bug in $() and getElementById

Reported by Ricky | June 9th, 2008 @ 08:17 PM

IE7 and Opera 9.27 (but not IE8, I'm told) will find <tag name="whatever"> when you are looking for <tag id="whatever">.

The easiest way to see the ie7 bug is the attached file animalcow.html, which is standalone except that it uses prototype.js. Use 1.6.0.2 to see the bug.

This fix overrides and replaces getElementById() to be W3C compliant (and ie8 compliant) and therefore fixes $().

The fix is in dom.js, in the beginning. The test code for ie7 and opera is in dom_test.js, and the html of the tests is in dom.html. All changes are in the attached patch.

I think I can attach 2 different files. We'll see.

The key is to use the non-standard document.all on browsers that have it. Luckily, this seems to be the same set as the browsers that have the bug.

I've tested the fixed prototype.js on Windows XP SP3 only, with

Firefox 3 RC2

IE7

Opera 9.27

Safari 3.1.1

All 4 browser say '24 assertions, 0 failures, 0 errors' for dom_test.html in testDollarFunction.

Credit is due to:

sixteen small stones

and to

web bug track

PROS:

Fixes the bug.

CONS:

My changes to dom_test.js seem verbose.

The code changes the dom routine, getElementById(), which could break some already broken code. You can change that by changing a function name and a couple-of-line edit to dom.js. This is a philosophical question for the prototype developers.

Comments and changes to this ticket

  • Ricky

    Ricky June 9th, 2008 @ 08:19 PM

    uploading animalcow.html

  • Ricky

    Ricky June 9th, 2008 @ 08:22 PM

    For IE8, this code will need to change, as it will needlessly (but harmlessly, I hope) over-ride getElementById().

  • Juriy Zaytsev

    Juriy Zaytsev June 10th, 2008 @ 01:56 AM

    Interesting. How does it affect performance?

  • Ricky

    Ricky June 10th, 2008 @ 08:20 AM

    The bug wrecked my app, which I consider poor performance. In other words, I haven't done any side-by-side performance testing.

    Subjectively, there is no difference in my small tests. The additional search loop only kicks in if the code discovers that the found element is not the right one, which should only happen, so far as I know, when the there is duplicate text for name and id, name="goober", and elsewhere id="goober".

    By the way, Microsoft has indirectly acknowledged that they have a misfeature (bug, to you and me). In the docs for ie7 the describe getElementById as matching on either name or id. But in the docs for ie8 they describe it as matching only on id. I consider this a confession. I like to imagine Ballmer, sobbing on the witness stand.

  • Tobie Langel

    Tobie Langel June 16th, 2008 @ 12:10 AM

    This has been discussed before and wasn't integrated into core for performance reasons.

    Whatever is decided this time, I'm not very keen on the idea of overwriting the existing `document.getelementById` method.

  • Tobie Langel

    Tobie Langel June 16th, 2008 @ 12:10 AM

      • → State changed from “new” to “enhancement”
  • Ricky

    Ricky June 21st, 2008 @ 10:11 PM

    If this has been discussed before it should be in the docs for $(). It isn't even in the Porteneuve book.

    Have performance tests been done? Are there results available?

    The only reason I did it by over-riding getElementById is that Microsoft has changed their implementation. Anyway, that is a side issue. $() is supposed to smooth over variations and defects among browsers; Prototype can have it's own private implementation. Else what is Prototype for?

    Should I submit a version that does not over-ride the standard function?

  • Juriy Zaytsev

    Juriy Zaytsev June 21st, 2008 @ 11:08 PM

    Changes like this is always a tough decision.

    On one hand, such quirks could waste hours of valuable time (if a developer is working on an outdated/developed-by-many-people project) and it is a library's job to prevent this from happening.

    On the other hand, one small change in $ could hog the entire library, bringing an app to its knees (performance-wise).

    We could solve problems like these with a global switch, separate function or an optional argument (passed to $).

    Introducing separate function is problematic - considering that $ is a global variable (and has such unique name).

    Optional argument to a $ is problematic as well - function already accepts N number of them, so these changes would break compatibility.

    Global switch seems like a pretty good solution. Something like $.IGNORE_NAMES = true; (which would be false by default)

    Frameworks like YUI already use global configuration. I'm not sure why prototype avoids this.

    Thoughts?

  • Ricky

    Ricky June 22nd, 2008 @ 07:07 AM

    Unless we do something, the $() is not working as advertised. This has got to be a bug. We should change the documentation or change the code.

    For IE7, the broken getElementById function actually works according to Microsoft's IE7 documentation, so one could argue it's not a bug. It violates the W3C spec, so you could argue the other way, too. I'll let the philosophers decide that one.

  • John-David Dalton

    John-David Dalton July 2nd, 2008 @ 03:10 PM

      • → Tag changed from “” to “dom ie ie6 ie7 needs_patch needs_tests opera”

    I don't see this as a big deal.

    Its a bug in the browser and any tinkering with it to try to fix it will only slow down this core method.

    We could do

    function $(element) {
      if (arguments.length > 1) {
        for (var i = 0, elements = [], length = arguments.length; i < length; i++)
          elements.push($(arguments[i]));
        return elements;
      }
      if (Object.isString(element)) {
        var id = element;
        element = document.getElementById(id);
    
        if (document.all && id === element.getAttribute('name') &&
            id !== element.getAttribute('id')) {
          var elements = document.all[id], length = elements.length || 0;
          while (length--) {
            if (elements[length].getAttribute('name') !== id &&
                elements[length].getAttribute('id') === id)
              return Element.extend(elements[length]);
          }
          return null;
        }
      }
      return Element.extend(element);
    }
    
  • Juriy Zaytsev

    Juriy Zaytsev July 2nd, 2008 @ 03:51 PM

    John,

    Using global switch gives control to the hands of developers.

    There's no slowdown unless you know what you are doing.

    Regarding browser bugs, 80% of what's in prototype.js is a workaround for them ;)

    I actually find this addition useful.

  • John-David Dalton

    John-David Dalton July 2nd, 2008 @ 06:20 PM

    Oh ya! I like switches too :)

Please Login or create a free account to add a new comment.

You can update this ticket by sending an email to from your email client. (help)

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

The Prototype JavaScript library.

Shared Ticket Bins