Is there any way to find an element in a documentFragment?
Asked Answered
P

8

26
var oFra = document.createDocumentFragment();
// oFra.[add elements];
document.createElement("div").id="myId";
oFra.getElementById("myId"); //not in FF

How can I get "myId" before attaching fragment to document?

Planetoid answered 29/10, 2009 at 12:24 Comment(1)
This question was asked in 2009. In 2012 we have querySelectorAll, which works on document fragments. See @Stephen's answer below.Foliolate
D
2

What about:

var oFra = document.createDocumentFragment();
var myDiv = document.createElement("div");
myDiv.id="myId";
oFra.appendChild(myDiv);
oFra.getElementById("myId"); //not in FF

Unless you've added the the created div to your document fragment I'm not sure why getElementById would find it?

--edit

If you're willing to roll your own getElementById function then you ought to be able to get the reference you're after, because this code works:

var oFra = document.createDocumentFragment();
var myDiv = document.createElement("div");
myDiv.id = "myId";
oFra.appendChild(myDiv);
if (oFra.hasChildNodes()) {
    var i=0;
    var myEl;
    var children = oFra.childNodes;
    for (var i = 0; i < children.length; i++) {
        if (children[i].id == "myId") {
            myEl = children[i];
        }
    }
}
window.alert(myEl.id);
Disentomb answered 29/10, 2009 at 12:30 Comment(8)
I was trying to avoid that solution, since i design a dataentry form in the fragment, add the events and attach to document. Works in IE. The reason seems that in IE fragment inherits from document, but in FF inherits from Node (W3C)Planetoid
OK, but if you're building the form in JS anyway, why not just keep references to the elements as you create them and use those references to add the events?Disentomb
There are many buttons, textboxes, combos etc in each and every form. I try to stick to the ids and minimize the dom object references where possible. I will attach first and assign events later.Planetoid
I must be missing something about what you're doing then - how are you creating elements and appending them to your document fragment without creating references to them? The tradeoff is between storing a reference to an element as you create it and not immediately throwing it away against scanning through the whole document a few milliseconds later in order to recreate that reference to the element.Disentomb
Edited the answer to fix the |myDiv = document.createElement("div").id = "myId"| line. In the original code |myDiv == "myId"|.Hymettus
IE's createDocumentFragment doesn't actually create a DocumentFragment, it creates a Document: msdn.microsoft.com/en-us/library/ms536387(VS.85).aspx (I like the way they say "This is defined in DOM Level 1" without explaining that they don't actually implement DocumentFragment in HTML and so the method returns the wrong type of object...)Wildeyed
The code only finds myEl if it's the root element or a child of it. Not if it's nested any deeper. Works for the simple example code in the question, but probably not for others searching for a getElementById replacement.Licentiate
@Licentiate Which is why it says just before that code "If you're willing to roll your own {{getElementById}} function then you ought to be able to get the reference you're after". The answer is not the code, the answer is to roll your own {{getElementById}}.Disentomb
D
33

All of these answers are rather old, from back when querySelectorAll and querySelector were not widely available. It should be noted that these two functions which accept CSS selectors as parameters do work on DocumentFragments in modern browsers, and should be the preferred way of dealing with the situation in the question. The alternate solutions presented in some of the answers would be a good approach for legacy browsers which did not support querySelectorAll or querySelector.

Here is an example usage:

var df = document.createDocumentFragment();
var div = document.createElement('div');
div.id = 'foo';
df.appendChild(div);
var result = df.querySelector('#foo'); // result contains the div element

A good implementation should first use object detection to see if the browser supports this. For instance:

function getElementByIdInFragment(fragment, id) {
    if (fragment.querySelector) {
        return fragment.querySelector('#' + id);
    } else {
        // your custom implementation here
    }
}
Deerskin answered 6/2, 2012 at 20:51 Comment(1)
I'm sure there is an other way, than to fetch back an element from documentfragment. Anyway you will lose all the benefit.Frasco
W
9

No. The DocumentFragment API is minimal to say the least: it defines no properties or methods, meaning that it only supports the properties and methods defined in the Node API. As methods such as getElementById are defined in the Document API, they cannot be used with a DocumentFragment.

Wildeyed answered 29/10, 2009 at 12:30 Comment(2)
Even though getElementById is defined in the Document API, it won't work on a newly created element that is not attached to the dom. myObj.getElementById doesn't work whereas myObj.getElementsByTagName works.Continuity
@Olivvv: that's to be expected: getElementById is a method of HTMLDocument w3.org/TR/REC-DOM-Level-1/level-one-html.html#ID-36113835, whereas getElementsByTagName is a method of both Document w3.org/TR/REC-DOM-Level-1/level-one-core.html#i-Document and Element w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-745549614 Therefore, getElementById will always throw an exception when an attempt is made to invoke it on an object of type Element whether that element is attached to the DOM or not.Wildeyed
M
8

NickFitz is right, DocumentFragment doesn't have the API you expect from Document or Element, in the standard or in browsers (which is a shame; it would be really handy to be able to set a fragment's innerHTML.

Even frameworks don't help you here, as they tend to require Nodes be in the document, or otherwise use methods on the context node that don't exist on fragments. You'd probably have to write your own, eg.:

 function Node_getElementById(node, id) {
      for (var i= 0; i<node.childNodes.length; i++) {
          var child= node.childNodes[i];
          if (child.nodeType!==1) // ELEMENT_NODE
              continue;
          if (child.id===id)
              return child;
          child= Node_getElementById(child, id);
          if (child!==null)
              return child;
      }
      return null;
 }

It would almost certainly be better to keep track of references as you go along than to rely on a naïve, poorly-performing function like the above.

var frag= document.createDocumentFragment();
var mydiv= document.createElement("div");
mydiv.id= 'myId';
frag.appendChild(mydiv);
// keep reference to mydiv
Multimillionaire answered 29/10, 2009 at 12:53 Comment(0)
D
2

What about:

var oFra = document.createDocumentFragment();
var myDiv = document.createElement("div");
myDiv.id="myId";
oFra.appendChild(myDiv);
oFra.getElementById("myId"); //not in FF

Unless you've added the the created div to your document fragment I'm not sure why getElementById would find it?

--edit

If you're willing to roll your own getElementById function then you ought to be able to get the reference you're after, because this code works:

var oFra = document.createDocumentFragment();
var myDiv = document.createElement("div");
myDiv.id = "myId";
oFra.appendChild(myDiv);
if (oFra.hasChildNodes()) {
    var i=0;
    var myEl;
    var children = oFra.childNodes;
    for (var i = 0; i < children.length; i++) {
        if (children[i].id == "myId") {
            myEl = children[i];
        }
    }
}
window.alert(myEl.id);
Disentomb answered 29/10, 2009 at 12:30 Comment(8)
I was trying to avoid that solution, since i design a dataentry form in the fragment, add the events and attach to document. Works in IE. The reason seems that in IE fragment inherits from document, but in FF inherits from Node (W3C)Planetoid
OK, but if you're building the form in JS anyway, why not just keep references to the elements as you create them and use those references to add the events?Disentomb
There are many buttons, textboxes, combos etc in each and every form. I try to stick to the ids and minimize the dom object references where possible. I will attach first and assign events later.Planetoid
I must be missing something about what you're doing then - how are you creating elements and appending them to your document fragment without creating references to them? The tradeoff is between storing a reference to an element as you create it and not immediately throwing it away against scanning through the whole document a few milliseconds later in order to recreate that reference to the element.Disentomb
Edited the answer to fix the |myDiv = document.createElement("div").id = "myId"| line. In the original code |myDiv == "myId"|.Hymettus
IE's createDocumentFragment doesn't actually create a DocumentFragment, it creates a Document: msdn.microsoft.com/en-us/library/ms536387(VS.85).aspx (I like the way they say "This is defined in DOM Level 1" without explaining that they don't actually implement DocumentFragment in HTML and so the method returns the wrong type of object...)Wildeyed
The code only finds myEl if it's the root element or a child of it. Not if it's nested any deeper. Works for the simple example code in the question, but probably not for others searching for a getElementById replacement.Licentiate
@Licentiate Which is why it says just before that code "If you're willing to roll your own {{getElementById}} function then you ought to be able to get the reference you're after". The answer is not the code, the answer is to roll your own {{getElementById}}.Disentomb
H
1

Using jQuery:

// Create DocumentFragment
var fragment  = document.createDocumentFragment(),
    container = document.createElement('div');

container.textContent = 'A div full of text!';
container.setAttribute('id', 'my-div-1');
container.setAttribute('class', 'a-div-class');
fragment.appendChild(container);

// Query container's class when given ID
var div = $('<div></div>').html(fragment);
console.log(div.find('#my-div-1').attr('class'));

jsFiddle: http://jsfiddle.net/CCkFs/

You have the overhead of creating the div with jQuery, though. A little hacky, but it works.

Herodotus answered 23/4, 2013 at 18:8 Comment(1)
$("<div> <div id='my-div-1' class='eek'></div></div>").find(#my-div-1") works. You just can't search for the root node.Decurved
H
1

The best way by far to find out what you can and can't do with a DocumentFragment is to examine its prototype:

const newFrag = document.createDocumentFragment();  
const protNewFrag = Object.getPrototypeOf( newFrag );
console.log( '£ protNewFrag:' ); 
console.log( protNewFrag ); 

I get

DocumentFragmentPrototype { getElementById: getElementById(), querySelector: querySelector(), querySelectorAll: querySelectorAll(), prepend: prepend(), append: append(), children: Getter, firstElementChild: Getter, lastElementChild: Getter, childElementCount: Getter, 1 more… }

... which tells me I can do things like:

const firstChild = newFrag.children[ 0 ];

PS this won't work:

const firstChild = Object.getPrototypeOf( newFrag ).children[ 0 ];

... you'll be told that "the object doesn't implement the DocumentFragment interface"

He answered 5/10, 2017 at 19:59 Comment(0)
C
0

An external source, listed below, showed the following code snippet:

var textblock=document.createElement("p")
textblock.setAttribute("id", "george")
textblock.setAttribute("align", "center")

Which displays a different way of setting the object's ID parameter.

Javascript Kit - Document Object Methods

Cheapskate answered 29/10, 2009 at 12:31 Comment(0)
U
0

My DOM has a #document-fragment under the element tag.

This is what I am using (using jQuery) , Also I have a use case where I have the HTML DOM in a string -

 var texttemplate = $(filecontents).find('template').html();


 $(texttemplate).children()

      <p>​Super produced One​</p>​, 
      <appler-one>​</appler-one>, 
      <p>​Super produced Two​</p>, 
      <appler-two>​…​</appler-two>]

$(texttemplate).html()

                "<p>Super produced One</p>
                <appler-one></appler-one>
                <p>Super produced Two</p>
                <appler-two>
                    <p>Super produced Three</p>
                    <appler-three></appler-three>
                </appler-two>"


$(texttemplate).find("appler-one")

              [<appler-one>​</appler-one>​]
Unable answered 24/8, 2014 at 17:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.