views:

264

answers:

2

The goal here is to manipulate some DOM nodes before they are inserted into the document, using prototypeJs methods.

Test cases:

<html>
<head>
 <script type="text/javascript" src="prototype.js" ></script>
</head>
<body>
 <script type="text/javascript">
  var elt = document.createElement("DIV");
  elt.id="root";
  elt.innerHTML="<div id='stuff'><span id='junk'></span></div>";

  console.info(elt);

  console.info(Element.down(elt, "#stuff"));
  console.info(Element.select(elt, "#stuff"));

  console.info(elt.down("#stuff"));
  console.info(elt.select("#stuff"));

  Element.extend(elt);

  console.info(elt.down("#stuff"));
  console.info(elt.select("#stuff"));

  document.body.appendChild(elt);

  console.info($("root").down("#stuff"));
  console.info($("root").select("#stuff"));

 </script>
</body>
</html>

In Firefox all 8 tests correctly output either the "stuff" div or a collection containing only the "stuff" div.

In ie (tested in 7/8) I would expect the second two tests only to fail as prototype does not automatically extend the DOM as in ff. However what actually happens is all the tests up to the point the element is inserted fail the two subsequent tests are fine. Once I call Element.extend if would expect the down / select methods to be available.

If this behaviour as expected and if so why?

How would you recommend I do my DOM traversal on nodes which are in memory in a cross browser friendly manner?


So thanks to Kaze no Koe, I've narrowed the issue down. It seems that this does work in ie but not for id selectors.

<html>
    <head>
        <script type="text/javascript" src="prototype.js" ></script>
    </head>
    <body>
        <script type="text/javascript">  
            var elt = document.createElement("DIV");
            elt.id="root";
            elt.innerHTML="<div id='stuff' class='junk'></div>";
            elt = $(elt);    

            console.info(elt.down("DIV"));      //fine
            console.info(elt.down(".junk"));    //fine
            console.info(elt.down("#stuff"));   //undefined in ie, fine in ff
        </script>
    </body>
</html>

It's not a problem for me to use class instead of id, so I can solve my original issue but for completeness sake can anyone explain why id selectors won't work before insertion in ie only? My guess would be that the ie implementation relies on document.getElementById whilst the ff one doesn't. Anyone confirm?

+1  A: 

Instead of:

Element.extend(elt);

Try:

elt = Element.extend(elt);

or

elt = $(elt);


As for how to do the traversing before you've inserted the node, here's some random examples that illustrate a few features of Prototype:

var elt = new Element('div', {
    className: 'someClass'
});

elt.insert(new Element('ul'));

var listitems = ['one', 'two', 'three'];

listitems.each(function(item){
    var elm = new Element('li');
    elm.innerHTML = item;
    elt.down('ul').insert(elm);
});

elt.getElementsBySelector('li'); //=> returns all LIs

elt.down('li'); //=> returns first li

elt.down('ul').down('li'); //=> also returns first li

elt.down('ul').down('li', 2); //=> should return the third if I'm not mistaken

// all before inserting it into the document!

Check the brand new API documentation.


Answering Ollie: my code above can be tested here, as you can see it works under IE 6.

Kaze no Koe
nice idea, but no this doesn't work either.
Ollie Edwards
Thanks for the examples, I'll look into this. Quick comment tho, you need to use className not class to avoid upsetting ie.
Ollie Edwards
Yeah, that one gets me every time :) Sorry
Kaze no Koe
So it does work unless you use an id selector (see edits to original post). Thanks for your help.
Ollie Edwards
You're welcome :) I didn't know about the id issue, so I learned something too.
Kaze no Koe
A: 

I don't think it is possible to select nodes that are not in the document, because the selector depends on the document node.

And you should build new elements this way :

var elt = new Element("div");
Fabien Ménager
Interesting. Do you know why this is different in ff?
Ollie Edwards
It isn't different, he's just wrong. You can create a whole page before inserting it into $(document.body) and it will work just as if it was already part of the document as long as you extend elements properly.
Kaze no Koe