getElementById 返回 null,但 getElementsByClassName 能正常工作,为什么?

3
我真的很想知道为什么getElementById返回null,而使用getElementsByClassName可以访问相同的元素。如果有人能帮助我理解这是什么原因就太好了。
请查看下面的代码,它是一个功能性的React组件,我将handleClose函数作为props传递,它关闭了模态框,但实际上它并不需要,所以我只放了Modal组件的代码。
import React, { useRef, useEffect } from 'react'

const A11yModal = ({ handleClose }) => {
  const focusClose = useRef(null)

  var closeBtn_cls = document.getElementsByClassName("btn-close")
   var closeIcon=document.getElementById('btnclose')

  useEffect(() => {
    focusClose.current.focus();
    console.log("using Id",closeIcon);
    console.log("using ClassName",closeBtn_cls);
    }, [])

  function onKeyPressed(e) {
    if (e.keyCode === 27) {
      handleClose()
    }
  }

  return (
    <div className="modal display-block">
      <section className="modal-main" role="dialog"
        aria-modal="true" onKeyDown={(e) => onKeyPressed(e)}>
        <button className="btn-close" id="btnclose" onClick={(e) => handleClose(e)} ref={focusClose}>
          X
        </button>
        <h1 id="modal_title" className="title" >Modal</h1>
        <div id="full_description" className="description" aria-describedby="full_description">
          <p>Description goes here.</p>
        </div>        
        <button className="close_btn" id="closebtn" onClick={(e) => handleClose(e)}> Close </button>     
      </section>
    </div>
  )
}

export default A11yModal

3
当这些方法运行时,函数没有发出 JSX。实际上,在几乎所有情况下,都不需要使用这些方法。React 应该在大多数情况下抽象出直接操作 DOM 的需求。考虑使用 ref - ggorlen
似乎OP已经在使用类似于ref={focusClose}的引用。 - palaѕн
此外,这种方式不适合显示模态框,如果您将其渲染在DOM层次结构的深处,您会发现自己与“z-Index”和“堆栈上下文”作斗争,这会使您的模态框呈现在其他一些元素的后面。考虑使用专为此目的设计的createPortal,或者使用一个库来实现。 - karianpour
2个回答

4
var closeBtn_cls = document.getElementsByClassName("btn-close")

在该代码行运行结束时,假设这是第一次渲染并且页面上没有其他具有该类名称的元素,则closeBtn_cls将为空(即该元素不存在)。但是closeBtn_cls是一个实时的HTMLCollection。这个类似数组的对象具有独特的属性,随着元素添加到DOM中,它会动态地更改。因此,在useEffect运行时,元素已经被添加到页面中,并更新了集合。 getElementById不返回HTMLCollection,因此不会动态更新。
虽然这解决了差异,但您也应该知道这不是在react中执行操作的推荐方式。在react中,您应该使用refs来获取对DOM元素的引用。您似乎已经意识到这一点,因为在示例中使用了refs,因此我建议删除使用getElementsByClassNamegetElementById的代码。

0

好的,使用className时看到元素的原因是在控制台上展开对象时对元素进行了评估,这种情况下是一个HTMLCollection,因此即使在初始渲染时没有元素存在,在执行useEffect后,您将在控制台中显示数据,从而初始化HTMLCollection并引用数组会导致值被看到,而使用id时直接返回单个元素,在初始渲染时没有元素存在。

const { useRef, useEffect } = React;

const A11yModal = ({ handleClose }) => {
  const focusClose = useRef(null)

  var closeBtn_cls = document.getElementsByClassName("btn-close")
   var closeIcon=document.getElementById('btnclose')
 console.log('class value', closeBtn_cls[0]);
  useEffect(() => {
    focusClose.current.focus();
    console.log("using Id",closeIcon);
    console.log("using ClassName",closeBtn_cls);
    }, [])

  function onKeyPressed(e) {
    if (e.keyCode === 27) {
      handleClose()
    }
  }

  return (
    <div className="modal display-block">
      <section className="modal-main" role="dialog"
        aria-modal="true" onKeyDown={(e) => onKeyPressed(e)}>
        <button className="btn-close" id="btnclose" onClick={(e) => handleClose(e)} ref={focusClose}>
          X
        </button>
        <h1 id="modal_title" className="title" >Modal</h1>
        <div id="full_description" className="description" aria-describedby="full_description">
          <p>Description goes here.</p>
        </div>        
        <button className="close_btn" id="closebtn" onClick={(e) => handleClose(e)}> Close </button>     
      </section>
    </div>
  )
}

ReactDOM.render(<A11yModal />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app" />


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接