1

I have the following code which simply parses HTML markup to a DOM object.

var html_str = '<div id="body-wrapper">\
        <div id="container-1">\
            <p>This is the first container - Line 1</p>\
            <p>This is the first container - Line 2</p>\
            <p><img src="assets/img/pull_1.jpg"></p>\
        </div>\
        <div id="container-2">\
            <p>This is the second container - Line 1</p>\
            <p>This is the second container - Line 2</p>\
            <p>This is the second container - Line 3</p>\
            <p><img src="assets/img/pull_2.jpg"></p>\
        </div>\
        <div id="container-3">\
            <p>Test</p>\
            <p><img src="assets/img/pull_3.jpg"></p>\
        </div>\
    </div>';

var elem_obj = document.createElement("div");
elem_obj.innerHTML = html_str;

How do I get the element with id == container-1 from within elem_obj? Vanilla JavaScript only and something other than elem_obj.querySelector('#container-1') because I need to support IE below version 8.

Thanks.

ObiHill
  • 11,448
  • 20
  • 86
  • 135
  • How did you get those multiline JS strings to work? – Sebastian Simon Apr 23 '15 at 01:40
  • Create a `DocumentFragment`. Then you can use `docFrag.getElementById`. http://stackoverflow.com/questions/1643349/is-there-any-way-to-find-an-element-in-a-documentfragment – Amadan Apr 23 '15 at 01:40
  • @Xufox: Doh. Too much jQuery, I almost never use plain DOM any more... Thanks, fixed. – Amadan Apr 23 '15 at 01:42
  • Maybe a loop over `elem_obj.children[0].children` and then that one where `elem_obj.children[0].children[i].id == 'container-1'`? It is supported in IE 6, 7, 8 but they just included comment nodes in `children` as well. – Sebastian Simon Apr 23 '15 at 01:46
  • Have you considered creating an iframe object and using that? – James Wilkins Apr 23 '15 at 01:47
  • @Amadan I'll look into that, but I'm not liking the age of that post. Perhaps I should stick with `querySelector` and look for an IE Polyfill – ObiHill Apr 23 '15 at 01:51
  • IDs should always be unique, so simply get the element by ID. – Etheryte Apr 23 '15 at 01:51
  • @Nit but OP asked to do it not from `document` but from `elem_obj`. – Sebastian Simon Apr 23 '15 at 01:52
  • @ObinwanneHill: What do you mean, you don't like the age of the post? Is it too new? [Even IE6 supports `DocumentFragment`](http://stackoverflow.com/questions/1353381/documentfragment-browser-support). Is it too old? That just proves it is reliable. – Amadan Apr 23 '15 at 02:03
  • @Amadan I'm not an age-ist or anything, but I tend to favor more contemporary solutions. `querySelector` is by far the best solution, I just can't find a way to polyfill it on an element level. – ObiHill Apr 23 '15 at 02:08
  • "I'm not ageist or anything, but spoons have been around forever, but it is the 21st century - there has to be a better way to enjoy soup!". Is there? For pure ID matching, `getElementById` is still best, as it is sufficient and faster than anything else. If you want more flexibility, you will need a library, since there is no way to implement the full functionality of `querySelector` in anything smaller than SizzleJS (and then you might as well just use jQuery), so it definitely leaves the realm of "Vanilla JavaScript". – Amadan Apr 23 '15 at 02:18
  • @Amadan Your analogy is faulty because a spoon is perfect for soup, and soup will always be soup forever, so the working equilibrium persists and does not elicit meaningless disruption. The relationship between Browsers and JavaScript, on the other hand, is less than ideal. Nothing wrong with trying to be contemporary. If that wasn't the case, nothing would be deprecated. – ObiHill Apr 23 '15 at 02:37
  • I assure you, `getElementById` is perfect for getting elements by ID. – Amadan Apr 23 '15 at 02:41
  • @Amadan Ok. So post an answer using `getElementById`, I'd love to see it. – ObiHill Apr 23 '15 at 02:47
  • Welp, I get to eat crow, because I wasn't paying attention - my apologies for arguing vehemently with the slightly wrong facts. IE6 has `getElementById`, but modern browsers do not. So the solution is the combination of the solutions in the linked question. I will compile shortly. – Amadan Apr 23 '15 at 04:00

2 Answers2

1

Proposed solution (alternative to DocumentFragment): you could search for the desired element with a loop.

JSFiddle (look at Console output).

The loop looks like this:

var elem_obj_containers = elem_obj.children[0].children;
  // All <div>s with those “container-” IDs

var container1_element=null; // The result or null

for(var i=0;i<=elem_obj_containers.length;i++){
    if(elem_obj_containers[i].id=='container-1'){ // Checking for ID
        container1_element=elem_obj_containers[i]; // Assigning result
        console.log(elem_obj_containers[i]); // Verify result
        break; // Show’s over!
    }
}

So if the ID is found, the element is assigned to container1_element, otherwise the variable remains null (like the real document.getElementById).

It should work all the way back to IE6 as of MDN’s page for children. IE 6, 7 and 8 just include HTML comment nodes in children as well. This shouldn’t be a big deal.

Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
  • 1
    No disrespect intended, but... Manual JavaScript DFS vs. direct C API of `getElementById`... I wouldn't call it an alternative. This is only useful as an educational exercise, I would never use this in production. (It's not even a proper DFS, it's limited to searching for the ID on the second level, so it is not even as general as `getElementById`.) – Amadan Apr 23 '15 at 02:25
  • Right, this is strongly dependent on the nesting level. Well, you could post an answer about `DocumentFragment` then or mark this question as duplicate of the other question (if it is). – Sebastian Simon Apr 23 '15 at 02:33
  • I believe it is a duplicate. The OP doesn't, and I was trying to convince him before using my superpowers. :p – Amadan Apr 23 '15 at 02:35
  • @Xufox Nice. I guess nesting level would be an issue, but hopefully it's nothing a little recursion won't solve. Thanks. – ObiHill Apr 23 '15 at 02:41
  • @ObinwanneHill “A little recursion” sounds a little bit like [“one little `GOTO`”](https://xkcd.com/292/) here… – Sebastian Simon Apr 23 '15 at 02:56
1

As I said in comments, apologies for arguing vehemently on incomplete data. While it is true old IE could use getElementById in DocumentFragment, it is not generally true for newer browsers, so a hybrid strategy is needed for best compatibility.

var html = '<div id="body-wrapper">\
  <div id="container-1">\
    <p>This is the first container - Line 1</p>\
    <p>This is the first container - Line 2</p>\
    <p><img src="#"></p>\
  </div>\
  <div id="container-2">\
    <p>This is the second container - Line 1</p>\
    <p>This is the second container - Line 2</p>\
    <p>This is the second container - Line 3</p>\
    <p><img src="#"></p>\
  </div>\
    <div id="container-3">\
    <p>Test</p>\
    <p><img src="#"></p>\
  </div>\
</div>';


// from http://stackoverflow.com/a/1643512/240443
function getElementByIdFromNode(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 = getElementByIdFromNode(child, id);
    if (child !== null)
      return child;
  }
  return null;
}
// based on http://stackoverflow.com/a/1643383/240443
function getElementByIdFromString(html, id) {
  var div = document.createElement("div");
  div.innerHTML = html;
  // New browsers
  if (div.querySelector) {
    return div.querySelector("#" + id);
  }
  // Old IE
  var frag = document.createDocumentFragment();
  if (frag.getElementById) {
    frag.appendChild(div);
    return frag.getElementById(id);
  }
  // Anything else just in case
  return getElementByIdFromNode(div, id);
}
var container3 = getElementByIdFromString(html, "container-3");
console.log(container3);
ObiHill
  • 11,448
  • 20
  • 86
  • 135
Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Hmmm...you did that yourself?! – ObiHill Apr 23 '15 at 09:07
  • Sorry, didn't see that. Thanks a lot for bringing everything together. It works like a charm. To test, I isolated each section [namely `querySelector`, `documentFragment`, and `getElementByIdFromNode`] and they all work so this should be good back to IE6. Cheers. – ObiHill Apr 23 '15 at 17:24