Почему jQuery или метод DOM, такой как getElementById, не находят элемент?

каковы возможные причины для document.getElementById, $("#id") или любой другой метод DOM / селектор jQuery не находит элементы?

пример проблемы включают в себя:

jQuery молча не удается связать обработчик событий и стандартный метод DOM, возвращающий null в результате ошибки:

Uncaught TypeError: не удается установить свойство '...'of null

6 ответов


элемент, который вы пытались найти не в дом когда ваш скрипт побежал.

позиция вашего DOM-зависимого скрипта может оказать глубокое влияние на его поведение. Браузеры разбирают HTML-документы сверху донизу. Элементы добавляются в DOM, и скрипты (как правило) выполняются по мере их появления. это означает, что порядок имеет значение. как правило, скрипты не можете найти элементы, которые появляются позже в разметке, потому что эти элементы еще предстоит добавить в DOM.

рассмотрим следующую разметку; скрипт #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>

Итак, что вы должны делать? У вас есть несколько вариантов:


Вариант 1: переместить скрипт

переместите скрипт дальше вниз по странице, непосредственно перед закрывающим тегом body. Организованный таким образом, остальная часть документа анализируется до того, как сценарий исполнено:

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

Примечание: размещение скриптов внизу обычно считается лучшие практики.


2: в jQuery ready()

отложить сценарий, пока дом был полностью разобран, используя ready():

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

Примечание: Вы можете просто привязать к DOMContentLoaded или window.onload но у каждого есть свои предостережения. в jQuery ready() предлагает гибридное решение.


Вариант 3: Делегирование Событий

делегированные события имеют то преимущество, что они могут обрабатывать события из дочерних элементов, которые добавляются в документ позже.

когда элемент вызывает событие (при условии, что это клокоча событие и ничто не остановит ее распространение), каждый родитель в родословной этого элемента также получает событие. Это позволяет присоединить обработчик к существующему элементу и выборке событий по мере их появления из его потомков... даже те, которые добавляются после присоединения обработчика. Все, что нам нужно сделать, это проверить событие, было ли оно вызвано желаемым элементом, и если да, запустите наш код.

jQuery on() выполняет эту логику для нас. Мы просто предоставляем имя события, селектор для желаемого потомок и обработчик событий:

<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>

Примечание: как правило, эта модель предназначена для элементов, которые не существовали во время загрузки или чтобы избежать прикрепления большого количества обработчиков. Стоит также отметить, что в то время как я приложил обработчик document (для демонстративных целей), вы должны выбрать ближайшего надежного предка.


Вариант 4:defer атрибут

использовать defer на <script>.

[defer, логический атрибут,] установлен, чтобы указать браузеру, что сценарий предназначен для выполнения после анализа документа.

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

Для справки, вот код из этой внешний скрипт:

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

Примечание: конечно кажется как волшебная пуля но важно знать о предостережениях...
1. defer можно использовать только для внешних скриптов, т. е. с .
2. будьте в курсе поддержка браузеров, т. е.: багги реализация в IE


коротко и просто: потому что элементы, которые вы ищете, не существует в документе (пока).


для остальной части этого ответа я буду использовать getElementById как пример, но то же самое относится к getElementsByTagName, querySelector и любой другой метод DOM, который выбирает элементы.

Возможные Причины

есть две причины, почему элемент не может exist:

  1. элемент с переданным идентификатором действительно не существует в документе. Вы должны дважды проверить, что ID вы передаете getElementById действительно соответствует идентификатору существующего элемента в (сгенерированном) HTML и что у вас нет с ошибкой идентификатор (идентификаторы регистр!).

    кстати, в большинство современных браузеров, которые реализуют querySelector() и querySelectorAll() методы, CSS-стиль нотация используется для извлечения элемента его id, например: document.querySelector('#elementID'), в отличие от метода, с помощью которого элемент извлекается его id под document.getElementById('elementID'); в первом # характер необходим, во втором он привел бы к не извлекаемому элементу.

  2. элемент не существует на данный момент вы называете getElementById.

последний случай довольно распространен. Браузеры анализируют и обработайте HTML сверху донизу. Это означает, что любой вызов элемента DOM, который происходит до того, как этот элемент DOM появится в HTML, завершится ошибкой.

рассмотрим следующий пример:

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

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

на div появляется после the script. На данный момент скрипт выполнен, элемент не существует пока и getElementById вернутся null.

jQuery

то же самое относится и ко всем селекторы с jQuery. jQuery не найдет элементы, если вы с ошибкой ваш выбор или вы пытаетесь выбрать их прежде чем они существуют на самом деле.

добавлен поворот, когда jQuery не найден, потому что вы загрузили скрипт без протокола и работаете из файловой системы:

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

этот синтаксис используется для загрузки скрипта через HTTPS на страницу с протоколом https: / / и для загрузки версии HTTP на страницу с протокол http://

он имеет неудачный побочный эффект попытки и неспособности загрузить file://somecdn.somewhere.com...


решений

прежде чем позвонить в getElementById (или любой метод DOM), убедитесь, что элементы, к которым вы хотите получить доступ, существуют, т. е. DOM загружен.

это можно обеспечить, просто поставив свой JavaScript после соответствующий элемент DOM

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

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

in вы также можете поместить код прямо перед закрывающим тегом body (</body>) (все элементы DOM будут доступны во время выполнения скрипта).

другие решения включают прослушивание load [MDN] или DOMContentLoaded [MDN] событий. В этих случаях не имеет значения, где в документе вы размещаете код JavaScript, вам просто нужно помнить, чтобы поместить все Код обработки DOM в обработчиках событий.

пример:

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

// or

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

смотрите статьи на quirksmode.org для получения дополнительной информации об обработке событий и различиях в браузере.

jQuery

сначала убедитесь, что jQuery-это правильно. используйте инструменты разработчика браузера чтобы узнать, был ли найден файл jQuery и исправить URL-адрес, если он не был (например, добавьте http: или https: схема в начале, отрегулируйте путь и т. д.)

слушать load/DOMContentLoaded события-это именно то, что jQuery делает с .ready() [docs]. Весь код jQuery, который влияет на элемент DOM, должен быть внутри этого обработчика событий.

в самом деле в jQuery учебник прямо говорится:

как почти все, что мы делаем при использовании jQuery читает или манипулирует объектной моделью документа (DOM), нам нужно убедиться, что мы начинаем добавлять события и т. д. как только дом будет готов.

для этого мы регистрируем события ready документа.

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

вы также можете использовать сокращенный синтаксис:

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

оба эквивалентны.


причины, почему селекторы на основе id не работают

  1. элемент / DOM с указанным идентификатором еще не существует.
  2. элемент существует, но он не зарегистрирован в DOM [в случае узлов HTML, динамически добавляемых из ответов Ajax].
  3. присутствует более одного элемента с одинаковым идентификатором, что вызывает конфликт.

решений

  1. попробуйте получить доступ к элементу после его декларации или, альтернативно, использовать такие вещи, как $(document).ready();

  2. для элементов, поступающих из ответов Ajax, используйте .bind() метод jQuery. Более старые версии jQuery имели .live() то же самое.

  3. используйте инструменты [например, плагин webdeveloper для браузеров], чтобы найти дубликаты идентификаторов и удалить их.


Как отметил @FelixKling, наиболее вероятным сценарием является то, что узлы, которые вы ищете, не существуют (пока).

однако современные методы разработки часто могут манипулировать элементами документа вне дерева документов либо с помощью DocumentFragments, либо просто отсоединяя/присоединяя текущие элементы напрямую. Такие методы могут использоваться как часть шаблонов JavaScript или во избежание чрезмерных операций перерисовки/перерисовки, когда рассматриваемые элементы сильно измененный.

аналогично, новая функциональность "Shadow DOM", развертываемая в современных браузерах, позволяет элементам быть частью документа, но не может запрашивать документ.getElementById и все его методы-братья (querySelector и т. д.). Это делается для инкапсуляции функциональности и ее скрытия.

опять же, скорее всего, элемент, который вы ищете, просто отсутствует (пока) в документе, и вы должны сделать так, как предлагает Феликс. Однако, вы следует также помнить, что это все чаще не единственная причина, по которой элемент может быть не найден (временно или постоянно).


Если элемент, к которому вы пытаетесь получить доступ, находится внутри iframe и вы пытаетесь получить к нему доступ вне контекста iframe Это также приведет к сбою.

Если вы хотите получить элемент в iframe, вы можете найти, как здесь.


положить document.getElementById на setTimeout()

Э. Г.

setTimeout(function(){
    console.log(document.getElementById('whatever'));
}, 100);

Если это работает, то это просто проблема времени.