Почему 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:
-
элемент с переданным идентификатором действительно не существует в документе. Вы должны дважды проверить, что ID вы передаете
getElementById
действительно соответствует идентификатору существующего элемента в (сгенерированном) HTML и что у вас нет с ошибкой идентификатор (идентификаторы регистр!).кстати, в большинство современных браузеров, которые реализуют
querySelector()
иquerySelectorAll()
методы, CSS-стиль нотация используется для извлечения элемента егоid
, например:document.querySelector('#elementID')
, в отличие от метода, с помощью которого элемент извлекается егоid
подdocument.getElementById('elementID')
; в первом#
характер необходим, во втором он привел бы к не извлекаемому элементу. элемент не существует на данный момент вы называете
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 не работают
- элемент / DOM с указанным идентификатором еще не существует.
- элемент существует, но он не зарегистрирован в DOM [в случае узлов HTML, динамически добавляемых из ответов Ajax].
- присутствует более одного элемента с одинаковым идентификатором, что вызывает конфликт.
решений
попробуйте получить доступ к элементу после его декларации или, альтернативно, использовать такие вещи, как
$(document).ready();
для элементов, поступающих из ответов Ajax, используйте
.bind()
метод jQuery. Более старые версии jQuery имели.live()
то же самое.используйте инструменты [например, плагин webdeveloper для браузеров], чтобы найти дубликаты идентификаторов и удалить их.
Как отметил @FelixKling, наиболее вероятным сценарием является то, что узлы, которые вы ищете, не существуют (пока).
однако современные методы разработки часто могут манипулировать элементами документа вне дерева документов либо с помощью DocumentFragments, либо просто отсоединяя/присоединяя текущие элементы напрямую. Такие методы могут использоваться как часть шаблонов JavaScript или во избежание чрезмерных операций перерисовки/перерисовки, когда рассматриваемые элементы сильно измененный.
аналогично, новая функциональность "Shadow DOM", развертываемая в современных браузерах, позволяет элементам быть частью документа, но не может запрашивать документ.getElementById и все его методы-братья (querySelector и т. д.). Это делается для инкапсуляции функциональности и ее скрытия.
опять же, скорее всего, элемент, который вы ищете, просто отсутствует (пока) в документе, и вы должны сделать так, как предлагает Феликс. Однако, вы следует также помнить, что это все чаще не единственная причина, по которой элемент может быть не найден (временно или постоянно).
Если элемент, к которому вы пытаетесь получить доступ, находится внутри iframe
и вы пытаетесь получить к нему доступ вне контекста iframe
Это также приведет к сбою.
Если вы хотите получить элемент в iframe, вы можете найти, как здесь.
положить document.getElementById
на setTimeout()
Э. Г.
setTimeout(function(){
console.log(document.getElementById('whatever'));
}, 100);
Если это работает, то это просто проблема времени.