TechnoCorner
TechnoCorner

Reputation: 5145

Implementing document.getElementById in javascript

I'm trying to implement native document.getElementById in javascript. I've implemented document.getElementsByClassName in javascript.

function getElementsByClassName (className) {
  var nodeList = [];
  function test(node) {
      if (node.classList && node.classList.contains(className)) {
        nodeList.push(node);
      }
      
      for (var index = 0; index < node.childNodes.length; index++) {
        test(node.childNodes[index]);
      }
      
      return nodeList;
  }
  
    test(document.body);
    
  return nodeList;
};

// Fails here.
function getElementById(className) {
    const result = [];
    
    function getEachIDNode(node) {
        if(node.contains(className)) {
            return node;
        }

        for(let i=0; i<node.childNodes.length; i++) {
            getEachIDNode(node.childNodes[i]);
        }

    }

    getEachIDNode(document.body);
}

console.log(getElementsByClassName('winner'));
console.log(getElementById('test'));
  <table>      
        <tr id="test">
            <td>#</td>
            <td class="winner">aa</td>
            <td>bb</td>
            <td>cc</td>
            <td>dd</td>
        </tr>
   </table>

   <table>      
        <tr>
            <td>#</td>
            <td class="winner">aa</td>
            <td>bb</td>
            <td>cc</td>
            <td>dd</td>
        </tr>
   </table>

   <table>      
        <tr>
            <td>#</td>
            <td class="winner">dd</td>
            <td>cc</td>
            <td>bb</td>
            <td>aa</td>
        </tr>
   </table>

I'm trying to understand how I can check if a node has an attribute ID.

Can someone enlighten me?

Upvotes: 3

Views: 2395

Answers (4)

Kaiido
Kaiido

Reputation: 136698

The native document.getElementById does not walk through the DOM tree searching for your Element, and that's why it is faster than other DOM selection methods.

Indeed, browsers have to keep a kind of a hash-map of all the Elements with an id in the active Document. So they just perform a look-up over this hash-map (which is not one) and return the element if they found it.

Thanks to IE </irony>, they do expose some of the entries of this hash-map as properties of the global window object.

So if you are going to make your own implementation, you can first check if this property returns your Element.
Unfortunately, it may happen that an Element's id do concur with an other property of the window object. So it may happen that we still need to walk the DOM.
In this case, use a TreeWalker, which is about the fastest API we have to walk through a DOM tree, moreover when we are only interested in some type of nodes (here Elements).

So all in all, a better implementation could look like this:

function getElementById(id) {
  if (!(id in window)) {
    console.log(id, 'not found');
    return null; // we are sure it's not set
  }
  // id maps are not marked as 'own property'
  if (!window.hasOwnProperty(id)) {
    if (window[id] instanceof Element &&
      window[id].id === id) { // it's our Element
      console.log(id, 'found in window');
      return window[id];
    }
    // in case of duplicate window[id] should return an HTMLCollection
    // (IIRC only Chrome does it correctly though)
    if (window[id] instanceof HTMLCollection &&
      window[id][0].id === id) {
      console.log(id, 'duplicate id is bad');
      return window[id][0];
    }
  }
  console.log(id, 'walking...');
  var walker = document.createTreeWalker(
    document.documentElement,
    NodeFilter.SHOW_ELEMENT,
    null,
    false
  );
  while (walker.nextNode()) {
    if (walker.currentNode.id === id) {
      return walker.currentNode;
    }
  }
  return null;
}
console.log(getElementById('foo'));
console.log(getElementById('unique'));
console.log(getElementById('duplicate'));
window.overwritten = 'oups';
console.log(getElementById('overwritten'));
<div id="unique">
  <div id="duplicate"></div>
  <div id="duplicate"></div>
  <div id="overwritten"></div>
</div>

As you can see, in this implementation we walk the DOM only if the window's property has been set to an other value, greatly improving the performances.

Upvotes: 1

Avani Somaiya
Avani Somaiya

Reputation: 348

To check if a node has an attribute ID.You have to write like this:

            var attr_check = $(".selector").attr('id')                              
            if(attr_check != undefined || attr_check != false)
            {
                console.log("this element has attribute id")
            }

You also write this code:

           var attr_check = document.getElementById('div-id').attr('id')    

instead of this:

           var attr_check = $(".selector").attr('id')   

Upvotes: 0

Akki
Akki

Reputation: 19

Check attribute property of DOM element.

function getElementById(id) {
    const result = [];

    function getEachIDNode(node) {
        if(!(node instanceof HTMLElement))
            return;

        if(node.hasAttribute('id') && node.getAttribute('id') === id) {
            result.push(node);
        }

        for(let i=0; i<node.childNodes.length; i++) {
            if(result.length > 0)
                return;
            getEachIDNode(node.childNodes[i]);
        }

    }
    getEachIDNode(document.body);
    return result[0];
}

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370729

Check the id property of the node against the passed argument (probably better to use id as an argument rather than className):

function getElementById(id) {
    const result = [];

    function getEachIDNode(node) {
        if(node.id === id) {
            result.push(node);
        }
        for(let i=0; i<node.childNodes.length; i++) {
            getEachIDNode(node.childNodes[i]);
        }
    }
    getEachIDNode(document.body);
    return result;
}
console.log(getElementById('subchild')[0].innerHTML);
<div id="parent">
  <div id="child1">
  </div>
  <div id="child2">
    <div id="subchild">
      subchild!
    </div>
  </div>
</div>

But if you actually want to replicate getElementById, don't try to return an array, return a single element :

function getElementById(id) {
  let match = null;
  const doFind = node => {
    if (!match && node.id === id) match = node;
    if (!match) return [...node.childNodes].find(doFind);
  }
  doFind(document.body);
  return match;
}
console.log(getElementById('subchild').innerHTML);
<div id="parent">
  <div id="child1">
  </div>
  <div id="child2">
    <div id="subchild">
      subchild!
    </div>
  </div>
</div>

Upvotes: 3

Related Questions