Javascript 为什么 jQuery 或诸如 getElementById 之类的 DOM 方法找不到元素?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14028959/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 15:29:04  来源:igfitidea点击:

Why does jQuery or a DOM method such as getElementById not find the element?

javascriptjquerydom

提问by Felix Kling

What are the possible reasons for document.getElementById, $("#id")or any other DOM method / jQuery selector not finding the elements?

什么是可能的原因document.getElementById$("#id")或任何其他DOM方法/ jQuery选择没有找到的元素?

Example problems include:

示例问题包括:

  • jQuery silently failing to bind an event handler
  • jQuery "getter" methods (.val(), .html(), .text()) returning undefined
  • A standard DOM method returning nullresulting in any of several errors:
  • jQuery 静默无法绑定事件处理程序
  • jQuery的“吸气”方法(.val().html().text())返回undefined
  • 返回的标准 DOM 方法会null导致以下几个错误中的任何一个:

Uncaught TypeError: Cannot set property '...' of null Uncaught TypeError: Cannot read property '...' of null

未捕获的 TypeError:无法设置 null 的属性“...”未捕获的 TypeError:无法读取 null 的属性“...”

The most common forms are:

最常见的形式是:

Uncaught TypeError: Cannot set property 'onclick' of null

Uncaught TypeError: Cannot read property 'addEventListener' of null

Uncaught TypeError: Cannot read property 'style' of null

未捕获的类型错误:无法设置 null 的属性“onclick”

未捕获的类型错误:无法读取 null 的属性“addEventListener”

未捕获的类型错误:无法读取 null 的属性“样式”

采纳答案by canon

The element you were trying to find wasn't in the DOMwhen your script ran.

当您的脚本运行时,您尝试查找的元素不在DOM 中

The position of your DOM-reliant script can have a profound effect upon its behavior. Browsers parse HTML documents from top to bottom. Elements are added to the DOM and scripts are (generally) executed as they're encountered. This means that order matters.Typically, scripts can't find elements which appear later in the markup because those elements have yet to be added to the DOM.

依赖 DOM 的脚本的位置可以对其行为产生深远的影响。浏览器从上到下解析 HTML 文档。元素被添加到 DOM 并且脚本(通常)在遇到它们时执行。这意味着顺序很重要。通常,脚本无法找到出现在标记后面的元素,因为这些元素尚未添加到 DOM 中。

Consider the following markup; script #1 fails to find the <div>while script #2 succeeds:

考虑以下标记;脚本 #1 无法找到<div>而脚本 #2 成功:

<script>
  console.log("script #1: %o", document.getElementById("test")); // null
</script>
<div id="test">test div</div>
<script>
  console.log("script #2: %o", document.getElementById("test")); // <div id="test" ...
</script>

So, what should you do? You've got a few options:

那你该怎么办?你有几个选择:



Option 1: Move your script

选项 1:移动脚本

Move your script further down the page, just before the closing body tag. Organized in this fashion, the rest of the document is parsed before your script is executed:

将您的脚本进一步向下移动到页面下方,就在结束正文标记之前。以这种方式组织,在执行脚本之前解析文档的其余部分:

<body>
  <button id="test">click me</button>
  <script>
    document.getElementById("test").addEventListener("click", function() {
      console.log("clicked: %o", this);
    });
  </script>
</body><!-- closing body tag -->

Note: Placing scripts at the bottom is generally considered a best practice.

注意:将脚本放在底部通常被认为是最佳实践



Option 2: jQuery's ready()

选项 2:jQuery 的 ready()

Defer your script until the DOM has been completely parsed, using $(handler):

推迟你的脚本,直到 DOM 被完全解析,使用:$(handler)

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
  $(function() {
    $("#test").click(function() {
      console.log("clicked: %o", this);
    });
  });
</script>
<button id="test">click me</button>

Note: You could simply bind to DOMContentLoadedor window.onloadbut each has its caveats. jQuery's ready()delivers a hybrid solution.

注意:您可以简单地绑定到DOMContentLoadedor 但每个都有其警告。jQuery提供了一个混合解决方案。window.onloadready()



Option 3: Event Delegation

选项 3:事件委托

Delegated eventshave the advantage that they can process events from descendant elements that are added to the document at a later time.

委托事件的优点是它们可以处理来自稍后添加到文档的后代元素的事件。

When an element raises an event (provided that it's a bubblingevent and nothing stops its propagation), each parent in that element's ancestry receives the event as well. That allows us to attach a handler to an existing element and sample events as they bubble up from its descendants... even those added after the handler is attached. All we have to do is check the event to see whether it was raised by the desired element and, if so, run our code.

当一个元素引发一个事件(假设它是一个冒泡事件并且没有什么可以阻止它的传播)时,该元素祖先中的每个父元素也会接收到该事件。这允许我们将处理程序附加到现有元素并在事件从其后代冒泡时采样事件......甚至是在附加处理程序之后添加的事件。我们所要做的就是检查事件以查看它是否由所需元素引发,如果是,则运行我们的代码。

jQuery's on()performs that logic for us. We simply provide an event name, a selector for the desired descendant, and an event handler:

jQueryon()为我们执行了该逻辑。我们只提供一个事件名称、一个所需后代的选择器和一个事件处理程序:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script>
  $(document).on("click", "#test", function(e) {
    console.log("clicked: %o",  this);
  });
</script>
<button id="test">click me</button>

Note: Typically, this pattern is reserved for elements which didn't exist at load-time orto avoid attaching a large amount of handlers. It's also worth pointing out that while I've attached a handler to document(for demonstrative purposes), you should select the nearest reliable ancestor.

注意:通常,此模式是为加载时不存在的元素保留的,或者是为了避免附加大量处理程序。还值得指出的是,虽然我已将处理程序附加到document(出于演示目的),但您应该选择最近的可靠祖先。



Option 4: The deferattribute

选项 4:defer属性

Use the deferattribute of <script>.

使用 的defer属性<script>

[defer, a Boolean attribute,] is set to indicate to a browser that the script is meant to be executed after the document has been parsed, but before firing DOMContentLoaded.

[ defer,一个布尔属性,] 被设置为向浏览器指示脚本将在文档被解析之后但在触发之前执行DOMContentLoaded

<script src="https://gh-canon.github.io/misc-demos/log-test-click.js" defer></script>
<button id="test">click me</button>

For reference, here's the code from that external script:

作为参考,这是来自该外部脚本的代码:

document.getElementById("test").addEventListener("click", function(e){
   console.log("clicked: %o", this); 
});

Note: The deferattribute certainly seemslike a magic bullet butit's important to be aware of the caveats...
1. defercan only be used for external scripts, i.e.: those having a srcattribute.
2. be aware of browser support, i.e.: buggy implementation in IE < 10

注意:该defer属性当然看起来像一个神奇的子弹,重要的是要注意警告...
1.defer只能用于外部脚本,即:那些具有src属性的脚本。
2. 注意浏览器支持,即:IE < 10 中的错误实现

回答by Felix Kling

Short and simple:Because the elements you are looking for do not exist in the document (yet).

简短而简单:因为您要查找的元素在文档中(还)不存在。



For the remainder of this answer I will use getElementByIdas example, but the same applies to getElementsByTagName, querySelectorand any other DOM method that selects elements.

对于这个答案的其余部分,我将getElementById用作示例,但这同样适用于getElementsByTagName,querySelector以及选择元素的任何其他 DOM 方法。

Possible Reasons

可能的原因

There are two reasons why an element might not exist:

元素可能不存在的原因有两个:

  1. An element with the passed ID really does not exist in the document. You should double check that the ID you pass to getElementByIdreally matches an ID of an existing element in the (generated) HTML and that you have not misspelledthe ID (IDs are case-sensitive!).

    Incidentally, in the majority of contemporary browsers, which implement querySelector()and querySelectorAll()methods, CSS-style notation is used to retrieve an element by its id, for example: document.querySelector('#elementID'), as opposed to the method by which an element is retrieved by its idunder document.getElementById('elementID'); in the first the #character is essential, in the second it would lead to the element not being retrieved.

  2. The element does not exist at the momentyou call getElementById.

  1. 文档中确实不存在具有传递 ID 的元素。您应该仔细检查您传递给的 ID 是否getElementById真正匹配(生成的)HTML 中现有元素的 ID,并且您没有拼错ID(ID区分大小写!)。

    顺便提及,在大多数当代的浏览器,其实现querySelector()querySelectorAll()方法,CSS样式符号用于检索由它的元件id,例如:document.querySelector('#elementID'),而不是通过该元件通过其检索的方法iddocument.getElementById('elementID'); 在第一个#字符是必不可少的,在第二个它会导致元素不被检索。

  2. 您调用 时该元素不存在getElementById

The latter case is quite common. Browsers parse and process the HTML from top to bottom. That means that any call to a DOM element which occurs before that DOM element appears in the HTML, will fail.

后一种情况很常见。浏览器从上到下解析和处理 HTML。这意味着在该 DOM 元素出现在 HTML 中之前发生的对该 DOM 元素的任何调用都将失败。

Consider the following example:

考虑以下示例:

<script>
    var element = document.getElementById('my_element');
</script>

<div id="my_element"></div>

The divappears afterthe script. At the moment the script is executed, the element does not exist yetand getElementByIdwill return null.

div出现script。在执行脚本的那一刻,元素不存在尚未getElementById返回null

jQuery

jQuery

The same applies to all selectors with jQuery. jQuery won't find elements if you misspelledyour selector or you are trying to select them before they actually exist.

这同样适用于所有带有 jQ​​uery 的选择器。如果你拼错了你的选择器或者你试图在它们实际存在之前选择它们jQuery 将不会找到它们

An added twist is when jQuery is not found because you have loaded the script without protocol and are running from file system:

一个额外的问题是当你没有找到 jQuery 时,因为你加载了没有协议的脚本并且正在从文件系统运行:

<script src="//somecdn.somewhere.com/jquery.min.js"></script>

this syntax is used to allow the script to load via HTTPS on a page with protocol https:// and to load the HTTP version on a page with protocol http://

此语法用于允许脚本通过 HTTPS 在协议为 https:// 的页面上加载,并在协议为 http:// 的页面上加载 HTTP 版本

It has the unfortunate side effect of attempting and failing to load file://somecdn.somewhere.com...

它具有尝试加载和加载失败的不幸副作用 file://somecdn.somewhere.com...



Solutions

解决方案

Before you make a call to getElementById(or any DOM method for that matter), make sure the elements you want to access exist, i.e. the DOM is loaded.

在您调用getElementById(或任何 DOM 方法)之前,请确保您要访问的元素存在,即 DOM 已加载。

This can be ensured by simply putting your JavaScript afterthe corresponding DOM element

这可以通过简单地将 JavaScript 放在相应的 DOM 元素之后来确保

<div id="my_element"></div>

<script>
    var element = document.getElementById('my_element');
</script>

in which case you can also put the code just before the closing body tag (</body>) (all DOM elements will be available at the time the script is executed).

在这种情况下,您还可以将代码放在结束正文标记 ( </body>) 之前(所有 DOM 元素将在脚本执行时可用)。

Other solutions include listening to the load[MDN]or DOMContentLoaded[MDN]events. In these cases it does not matter where in the document you place the JavaScript code, you just have to remember to put all DOM processing code in the event handlers.

其他解决方案包括侦听load[MDN]DOMContentLoaded[MDN]事件。在这些情况下,将 JavaScript 代码放置在文档中的哪个位置并不重要,您只需要记住将所有 DOM 处理代码放在事件处理程序中。

Example:

例子:

window.onload = function() {
    // process DOM elements here
};

// or

// does not work IE 8 and below
document.addEventListener('DOMContentLoaded', function() {
    // process DOM elements here
});

Please see the articles at quirksmode.orgfor more information regarding event handling and browser differences.

有关事件处理和浏览器差异的更多信息,请参阅quirksmode.org 上文章

jQuery

jQuery

First make sure that jQuery is loaded properly. Use the browser's developer toolsto find out whether the jQuery file was found and correct the URL if it wasn't (e.g. add the http:or https:scheme at the beginning, adjust the path, etc.)

首先确保 jQuery 已正确加载。使用浏览器的开发者工具找出jQuery文件是否找到,如果没有则更正URL(例如在开头添加http:https:方案,调整路径等)

Listening to the load/DOMContentLoadedevents is exactly what jQuery is doing with .ready()[docs]. All your jQuery code that affects DOM element should be inside that event handler.

监听load/DOMContentLoaded事件正是 jQuery 对.ready()[docs]所做的。所有影响 DOM 元素的 jQuery 代码都应该在该事件处理程序中。

In fact, the jQuery tutorialexplicitly states:

事实上,jQuery 教程明确指出:

As almost everything we do when using jQuery reads or manipulates the document object model (DOM), we need to make sure that we start adding events etc. as soon as the DOM is ready.

To do this, we register a ready event for the document.

$(document).ready(function() {
   // do stuff when DOM is ready
});

由于我们在使用 jQuery 时所做的几乎所有事情都读取或操作文档对象模型 (DOM),因此我们需要确保在 DOM 准备就绪后立即开始添加事件等。

为此,我们为文档注册了一个就绪事件。

$(document).ready(function() {
   // do stuff when DOM is ready
});

Alternatively you can also use the shorthand syntax:

或者,您也可以使用速记语法:

$(function() {
    // do stuff when DOM is ready
});

Both are equivalent.

两者是等价的。

回答by sumit

Reasons why id based selectors don't work

基于 id 的选择器不起作用的原因

  1. The element/DOM with id specified doesn't exist yet.
  2. The element exists, but it is not registered in DOM [in case of HTML nodes appended dynamically from Ajax responses].
  3. More than one element with the same id is present which is causing a conflict.
  1. 指定 id 的元素/DOM 尚不存在。
  2. 该元素存在,但未在 DOM 中注册[在从 Ajax 响应动态附加 HTML 节点的情况下]。
  3. 存在多个具有相同 ID 的元素,这会导致冲突。

Solutions

解决方案

  1. Try to access the element after its declaration or alternatively use stuff like $(document).ready();

  2. For elements coming from Ajax responses, use the .bind()method of jQuery. Older versions of jQuery had .live()for the same.

  3. Use tools [for example, webdeveloper plugin for browsers] to find duplicate ids and remove them.

  1. 尝试在声明后访问该元素,或者使用类似的东西 $(document).ready();

  2. 对于来自 Ajax 响应的元素,使用.bind()jQuery的方法。旧版本的 jQuery.live()也是如此。

  3. 使用工具 [例如,浏览器的 webdeveloper 插件] 查找重复的 ID 并将其删除。

回答by George Mulligan

If the element you are trying to access is inside an iframeand you try to access it outside the context of the iframethis will also cause it to fail.

如果您尝试访问的元素在 an 内部,iframe而您尝试在 the 上下文之外访问它,iframe这也会导致它失败。

If you want to get an element in an iframe you can find out how here.

如果你想在 iframe 中获取一个元素,你可以在这里找到方法。

回答by Nathan Bubna

As @FelixKling pointed out, the most likely scenario is that the nodes you are looking for do not exist (yet).

正如@FelixKling 指出的那样,最可能的情况是您正在寻找的节点(尚不存在)。

However, modern development practices can often manipulate document elements outside of the document tree either with DocumentFragments or simply detaching/reattaching current elements directly. Such techniques may be used as part of JavaScript templating or to avoid excessive repaint/reflow operations while the elements in question are being heavily altered.

但是,现代开发实践通常可以使用 DocumentFragments 或简单地直接分离/重新附加当前元素来操作文档树之外的文档元素。此类技术可用作 JavaScript 模板的一部分,或在相关元素被大量更改时避免过多的重绘/回流操作。

Similarly, the new "Shadow DOM" functionality being rolled out across modern browsers allows elements to be part of the document, but not query-able by document.getElementById and all of its sibling methods (querySelector, etc.). This is done to encapsulate functionality and specifically hide it.

类似地,在现代浏览器中推出的新“Shadow DOM”功能允许元素成为文档的一部分,但不能通过 document.getElementById 及其所有同级方法(querySelector 等)进行查询。这样做是为了封装功能并专门隐藏它。

Again, though, it is most likely that the element you are looking for simply is not (yet) in the document, and you should do as Felix suggests. However, you should also be aware that that is increasingly not the only reason that an element might be unfindable (either temporarily or permanently).

不过,很可能您要查找的元素根本(还)不在文档中,您应该按照 Felix 的建议进行操作。但是,您还应该意识到,这越来越不是元素可能无法找到(暂时或永久)的唯一原因。

回答by CertainPerformance

If script execution order is not the issue, another possible cause of the problem is that the element is not being selected properly:

如果脚本执行顺序不是问题,则问题的另一个可能原因是未正确选择元素:

  • getElementByIdrequires the passed string to be the ID verbatim, and nothing else. If you prefix the passed string with a #, and the ID does not start with a #, nothing will be selected:

    <div id="foo"></div>
    
    // Error, selected element will be null:
    document.getElementById('#foo')
    // Fix:
    document.getElementById('foo')
    
  • Similarly, for getElementsByClassName, don't prefix the passed string with a .:

    <div class="bar"></div>
    
    // Error, selected element will be undefined:
    document.getElementsByClassName('.bar')[0]
    // Fix:
    document.getElementsByClassName('bar')[0]
    
  • With querySelector, querySelectorAll, and jQuery, to match an element with a particular class name, put a .directly before the class. Similarly, to match an element with a particular ID, put a #directly before the ID:

    <div class="baz"></div>
    
    // Error, selected element will be null:
    document.querySelector('baz')
    $('baz')
    // Fix:
    document.querySelector('.baz')
    $('.baz')
    

    The rules here are, in most cases, identical to those for CSS selectors, and can be seen in detail here.

  • To match an element which has two or more attributes (like two class names, or a class name and a data-attribute), put the selectors for each attribute next to each other in the selector string, withouta space separating them (because a space indicates the descendant selector). For example, to select:

    <div class="foo bar"></div>
    

    use the query string .foo.bar. To select

    <div class="foo" data-bar="someData"></div>
    

    use the query string .foo[data-bar="someData"]. To select the <span>below:

    <div class="parent">
      <span data-username="bob"></span>
    </div>
    

    use div.parent > span[data-username="bob"].

  • Capitalization and spelling does matterfor all of the above. If the capitalization is different, or the spelling is different, the element will not be selected:

    <div class="result"></div>
    
    // Error, selected element will be null:
    document.querySelector('.results')
    $('.Result')
    // Fix:
    document.querySelector('.result')
    $('.result')
    
  • You also need to make sure the methods have the proper capitalization and spelling. Use one of:

    $(selector)
    document.querySelector
    document.querySelectorAll
    document.getElementsByClassName
    document.getElementsByTagName
    document.getElementById
    

    Any other spelling or capitalization will not work. For example, document.getElementByClassNamewill throw an error.

  • Make sure you pass a string to these selector methods. If you pass something that isn't a string to querySelector, getElementById, etc, it almost certainly won't work.

  • getElementById要求传递的字符串是 ID逐字,没有别的。如果您在传递的字符串前面加上 a #,并且 ID 不以 a 开头#,则不会选择任何内容:

    <div id="foo"></div>
    
    // Error, selected element will be null:
    document.getElementById('#foo')
    // Fix:
    document.getElementById('foo')
    
  • 同样,对于getElementsByClassName,不要在传递的字符串前加上前缀.

    <div class="bar"></div>
    
    // Error, selected element will be undefined:
    document.getElementsByClassName('.bar')[0]
    // Fix:
    document.getElementsByClassName('bar')[0]
    
  • 使用 querySelector、querySelectorAll 和 jQuery,要匹配具有特定类名的元素,请.直接在类之前放置一个。类似地,要匹配具有特定 ID 的元素,请#直接在 ID 之前放置一个:

    <div class="baz"></div>
    
    // Error, selected element will be null:
    document.querySelector('baz')
    $('baz')
    // Fix:
    document.querySelector('.baz')
    $('.baz')
    

    在大多数情况下,这里的规则与 CSS 选择器的规则相同,可以在此处查看详细信息

  • 要匹配具有两个或多个属性(例如两个类名,或一个类名和一个data-属性)的元素,请将每个属性的选择器在选择器字符串中彼此相邻放置,不要用空格分隔它们(因为空格表示的后代选择)。例如,要选择:

    <div class="foo bar"></div>
    

    使用查询字符串.foo.bar。选择

    <div class="foo" data-bar="someData"></div>
    

    使用查询字符串.foo[data-bar="someData"]。要选择<span>以下内容:

    <div class="parent">
      <span data-username="bob"></span>
    </div>
    

    使用div.parent > span[data-username="bob"].

  • 资本和拼写事情做上述所有的。如果大小写不同,或者拼写不同,元素将不会被选中:

    <div class="result"></div>
    
    // Error, selected element will be null:
    document.querySelector('.results')
    $('.Result')
    // Fix:
    document.querySelector('.result')
    $('.result')
    
  • 您还需要确保这些方法具有正确的大小写和拼写。使用以下之一:

    $(selector)
    document.querySelector
    document.querySelectorAll
    document.getElementsByClassName
    document.getElementsByTagName
    document.getElementById
    

    任何其他拼写或大写都不起作用。例如,document.getElementByClassName会抛出错误。

  • 确保将字符串传递给这些选择器方法。如果传递的东西是不是一个字符串querySelectorgetElementById等等,几乎可以肯定是行不通的。

回答by Jatinder Kumar

When I tried your code, it worked.

当我尝试您的代码时,它起作用了。

The only reason that your event is not working, may be that your DOM was not ready and your button with id "event-btn" was not yet ready. And your javascript got executed and tried to bind the event with that element.

您的事件无法正常工作的唯一原因可能是您的 DOM 尚未准备好并且您的 id 为“event-btn”的按钮尚未准备好。并且您的 javascript 被执行并尝试将事件与该元素绑定。

Before using the DOM element for binding, that element should be ready. There are many options to do that.

在使用 DOM 元素进行绑定之前,该元素应该准备好。有很多选择可以做到这一点。

Option1: You can move your event binding code within document ready event. Like:

选项 1:您可以在文档就绪事件中移动您的事件绑定代码。喜欢:

document.addEventListener('DOMContentLoaded', (event) => {
  //your code to bind the event
});

Option2: You can use timeout event, so that binding is delayed for few seconds. like:

选项2:您可以使用超时事件,使绑定延迟几秒钟。喜欢:

setTimeout(function(){
  //your code to bind the event
}, 500);

Option3: move your javascript include to the bottom of your page.

选项 3:将您的 javascript 包含移动到页面底部。

I hope this helps you.

我希望这可以帮助你。

回答by Mia Davis

the problem is that the dom element 'speclist' is not created at the time the javascript code is getting executed. So I put the javascript code inside a function and called that function on body onload event.

问题是在执行 javascript 代码时未创建 dom 元素“speclist”。所以我将 javascript 代码放在一个函数中,并在 body onload 事件上调用该函数。

function do_this_first(){
   //appending code
}

<body onload="do_this_first()">
</body>

回答by goku_da_master

My solution was to not use the id of an anchor element: <a id='star_wars'>Place to jump to</a>. Apparently blazor and other spa frameworks have issues jumping to anchors on the same page. To get around that I had to use document.getElementById('star_wars'). However this didn't work until I put the id in a paragraph element instead: <p id='star_wars'>Some paragraph<p>.

我的解决方案是不使用锚元素的 id: <a id='star_wars'>Place to jump to</a>。显然 blazor 和其他 spa 框架在跳转到同一页面上的锚点时存在问题。为了解决这个问题,我不得不使用document.getElementById('star_wars'). 但是,直到我将 id 放在一个段落元素中,这才起作用:<p id='star_wars'>Some paragraph<p>.

Example using bootstrap:

使用引导程序的示例:

<button class="btn btn-link" onclick="document.getElementById('star_wars').scrollIntoView({behavior:'smooth'})">Star Wars</button>

... lots of other text

<p id="star_wars">Star Wars is an American epic...</p>