it-swarm.com.ru

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

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

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

  • jQuery молча не удается связать обработчик событий
  • jQuery методы "getter" (.val(), .html(), .text()), возвращающие undefined
  • Стандартный метод DOM, возвращающий null, что приводит к любой из нескольких ошибок:

    Uncaught TypeError: Невозможно установить свойство '...' с нулевым значением Uncaught TypeError: Невозможно прочитать свойство '...' с нулевым значением

    наиболее распространенные формы:

    Uncaught TypeError: Невозможно установить свойство 'onclick' для null

    Uncaught TypeError: Невозможно прочитать свойство 'addEventListener' с нулевым значением

    Uncaught TypeError: Невозможно прочитать свойство 'style' из null

424
Felix Kling

Элемент, который вы пытались найти, не был в DOM во время работы вашего скрипта.

Положение вашего 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: ready() в jQuery

Отложите ваш сценарий, пока DOM не будет полностью проанализирован, используя 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: делегирование событий

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

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

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

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

427
canon

Коротко и просто: Поскольку искомые элементы не существуют в документе (пока).


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

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

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

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

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

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

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

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

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

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

divпоявляется после scriptname__. На момент выполнения сценария элемент не существует пока и getElementByIdвернет nullname__.

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>

в этом случае вы также можете поместить код непосредственно перед закрывающим тегом 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: в начале, измените путь и т.д.)

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

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

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

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

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

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

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

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

139
Felix Kling

Причины, по которым селекторы на основе идентификатора не работают

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

Solutions

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

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

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

13
sumit

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

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

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

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

11
Nathan Bubna

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

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

10
George Mulligan

Если порядок выполнения скрипта не является проблемой, другой возможной причиной проблемы является то, что элемент выбран неправильно:

  • getElementById требует, чтобы переданная строка была ID дословно , и ничего больше. Если вы передаете переданную строку с #, а идентификатор не начинается с #, ничего не будет выбрано:

    <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, чтобы сопоставить элемент с определенным именем класса, поместите . непосредственно перед классом. Аналогично, чтобы сопоставить элемент с определенным идентификатором, поместите # непосредственно перед идентификатором:

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

    Правила здесь, в большинстве случаев, идентичны правилам для селекторов CSS, и их можно увидеть подробно здесь .

  • Заглавные буквы и орфография имеют значение для всего вышеперечисленного. Если заглавная буква отличается или орфография отличается, элемент не будет выбран:

    <div class="result"></div>
    
    // Error, selected element will be null:
    document.querySelector('.results')
    $('.Result')
    // Fix:
    document.querySelector('.result')
    $('.result')
    
2
CertainPerformance