【问题标题】:Implementing document.getElementById in javascript在 javascript 中实现 document.getElementById
【发布时间】:2018-07-24 04:34:38
【问题描述】:

我正在尝试在 javascript 中实现原生 document.getElementById。我已经在 javascript 中实现了document.getElementsByClassName

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>

我正在尝试了解如何检查节点是否具有属性 ID。

谁能启发我?

【问题讨论】:

  • 添加你的html代码,如果可能的话,请提供可运行的代码
  • 您可以直接查看node.id === className。虽然,classNamegetElementById 的一个奇怪的参数名称。
  • @VicJordan 完成。请检查。我添加了我的实现。

标签: javascript dom


【解决方案1】:

根据传递的参数检查节点的id 属性(使用id 作为参数可能比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>

但是如果你实际上想要复制getElementById,不要尝试返回数组,返回单个元素 :

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>

【讨论】:

  • 好奇QQ:既然知道ID是唯一的,为什么还要有结果数组呢?我们不能直接从 if 语句中返回吗?
  • 是的,如果你真的想复制getElementById,最好返回一个元素。数组在这里没有多大意义(您尝试使用它有什么原因吗?)
  • 啊,不是这样。后来我重新访问了我的代码,然后我有了这个想法!谢谢你的回答。
  • @TechnoCorner 如果您觉得有帮助,请不要忘记接受并投票。
【解决方案2】:

检查DOM元素的属性。

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];
}

【讨论】:

  • 所以&lt;svg&gt; 元素及其子元素被排除在外了吗?
  • No 不执行,因为 不是 instanceof HTMLElement.
【解决方案3】:

原生 document.getElementById 不会遍历 DOM 树来搜索您的元素,这就是它比其他 DOM 选择方法更快的原因。

确实,浏览器必须在活动文档中保留所有具有 id 的元素的哈希映射。所以他们只是对这个 hash-map (它不是一个)执行查找,如果找到它就返回元素。

感谢 IE ,他们确实将这个 hash-map 的一些条目公开为全局 window 对象的属性。

因此,如果您要进行自己的实现,您可以先检查此属性是否返回您的元素。
不幸的是,元素的 id 可能与 window 对象的其他属性一致。因此,我们可能仍然需要遍历 DOM。
在这种情况下,使用TreeWalker,这是我们必须通过 DOM 树最快的 API,此外,当我们只对某种类型的节点(此处为元素)感兴趣时。

总而言之,一个更好的实现应该是这样的:

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>

如您所见,在此实现中,只有当窗口的属性已设置为其他值时,我们才会遍历 DOM,从而大大提高了性能。

【讨论】:

    【解决方案4】:

    要检查一个节点是否有属性ID。你必须这样写:

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

    你也写了这段代码:

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

    而不是这个:

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

    【讨论】:

      猜你喜欢
      • 2017-11-19
      • 1970-01-01
      • 2014-08-24
      • 1970-01-01
      • 2010-10-31
      • 2020-10-04
      • 2023-03-25
      • 2010-12-20
      • 1970-01-01
      相关资源
      最近更新 更多