jQuery 또는 getElementById와 같은 DOM 메소드가 요소를 찾지 못하는 이유는 무엇입니까?


483

무엇에 대한 가능한 이유는 document.getElementById, $("#id")또는 기타 DOM 방법 / jQuery를 선택자는 요소를 발견하지?

문제의 예는 다음과 같습니다.

  • jQuery가 자동으로 이벤트 핸들러를 바인드하지 못함
  • jQuery를 "게터"방법 ( .val(), .html(), .text()) 반환undefined
  • null몇 가지 오류가 발생하여 표준 DOM 메서드가 반환 되었습니다.

잡히지 않은 TypeError : null의 속성 '...'을 설정할 수 없습니다 잡히지 않은 TypeError : null의 속성 '...'을 읽을 수 없습니다

가장 일반적인 형태는 다음과 같습니다.

잡히지 않은 TypeError : null 속성 'onclick'을 설정할 수 없습니다

잡히지 않은 TypeError : null의 'addEventListener'속성을 읽을 수 없습니다

잡히지 않은 TypeError : null의 'style'속성을 읽을 수 없습니다


32
특정 DOM 요소를 찾을 수없는 이유와 JavaScript 코드가 DOM 요소 앞에 있기 때문에 그 이유가 자주 있습니다. 이것은 이러한 유형의 질문에 대한 정식 답변을위한 것입니다. 커뮤니티 위키이므로 자유롭게 개선하십시오 .
Felix Kling

답변:


481

스크립트를 실행할 때 찾으려는 요소가 DOM 에 없었 습니다.

DOM 의존 스크립트의 위치는 동작에 큰 영향을 줄 수 있습니다. 브라우저는 HTML 문서를 위에서 아래로 구문 분석합니다. 요소가 DOM에 추가되고 스크립트는 일반적으로 발생하는대로 실행됩니다. 이것은 순서가 중요하다는 것을 의미합니다. 일반적으로 스크립트는 마크 업에서 나중에 표시되는 요소를 찾을 수 없습니다. 이러한 요소는 아직 DOM에 추가되지 않았기 때문입니다.

다음 마크 업을 고려하십시오. 스크립트 # <div>2가 성공 하는 동안 스크립트 # 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()

다음을 사용하여 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>

참고 : 당신은 단순히 결합 할 수 DOMContentLoaded또는 그러나 각각의주의 사항이 있습니다. jQuery 는 하이브리드 솔루션을 제공합니다.window.onloadready()


옵션 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, 부울 속성]은 브라우저가 문서를 구문 분석 한 후 실행하기 전에 스크립트가 실행되도록 브라우저에 표시하도록 설정되어 있습니다 DOMContentLoaded.

<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의 버그 구현


이제 2020 년입니다. "맨 아래에 스크립트 배치"가 여전히 "모범 사례로 간주"됩니까? 나는 (현재) 모든 리소스를 넣고 스크립트를 <head>사용 defer합니다 (오래된 호환되지 않는 브라우저를 지원할 필요는 없습니다)
Stephen P

나는 현대 브라우저로 옮기는 것을지지하는 사람입니다. 즉,이 연습은 실제로 비용이 들지 않으며 어디에서나 작동합니다. 반면에, 모두 defer와하는 async일부 훨씬 나이 브라우저 버전에 매우 광범위한 지원 -even에 도달 다시이있다. 그들은 의도 된 행동을 명확하고 명시 적으로 신호하는 추가 이점이 있습니다. 이러한 속성은 src속성을 지정하는 스크립트 ( 예 : 외부 스크립트) 에서만 작동합니다 . 이점을 측정하십시오. 어쩌면 둘 다 사용할까요? 당신의 전화.
캐논

146

짧고 단순함 : 찾고있는 요소가 문서에 존재하지 않기 때문에 (아직)


이 응답의 나머지 I가 사용 getElementById예로서,하지만 동일한 적용 getElementsByTagName, querySelector및 기타 DOM 방법을 선택하는 요소.

가능한 이유

요소가 존재하지 않는 데는 두 가지 이유가 있습니다.

  1. 전달 된 ID를 가진 요소가 문서에 실제로 존재하지 않습니다. 전달한 getElementByIdID가 (생성 된) HTML에있는 기존 요소의 ID와 실제로 일치하고 ID 철자를 잘못 입력 하지 않았는지 다시 한 번 확인해야합니다 (ID는 대소 문자를 구분합니다 !).

    또한, 구현 및 메소드 를 구현 하는 대부분의 최신 브라우저 에서 CSS 스타일 표기법은 예를 들어 다음 과 같이 요소를 검색하는 데 사용 됩니다 . 첫 번째 문자는 필수 요소이며 두 번째 문자는 검색되지 않는 요소입니다.querySelector()querySelectorAll()iddocument.querySelector('#elementID')iddocument.getElementById('elementID')#

  2. 전화를 거는 순간 요소가 존재하지 않습니다 getElementById.

후자의 경우는 매우 일반적입니다. 브라우저는 HTML을 위에서 아래로 구문 분석하고 처리합니다. 이는 해당 DOM 요소가 HTML에 나타나기 전에 발생하는 DOM 요소에 대한 호출이 실패 함을 의미합니다.

다음 예제를 고려하십시오.

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

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

div나타납니다 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이로드 됨).

이것은 단순히 자바 스크립트를 넣어 보장 할 수 해당 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:스키마 추가 , 경로 조정 등).

load/ DOMContentLoaded 이벤트를 듣는 것은 jQuery가 .ready() [docs] 로하는 일과 정확히 같습니다 . DOM 요소에 영향을 미치는 모든 jQuery 코드는 해당 이벤트 핸들러 내에 있어야합니다.

실제로 jQuery 튜토리얼 은 다음과 같이 명시 적으로 설명합니다.

jQuery를 사용할 때 수행하는 거의 모든 것이 DOM (Document Object Model)을 읽거나 조작하므로 DOM이 준비 되 자마자 이벤트 등을 추가하기 시작해야합니다.

이를 위해 문서에 대한 준비된 이벤트를 등록합니다.

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

또는 속기 구문을 사용할 수도 있습니다.

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

둘 다 동일합니다.


1
ready"최신"선호 구문을 반영하기 위해 이벤트에 대한 마지막 부분을 수정해야 합니까, 아니면 이전 버전에서 작동하도록 그대로 유지하는 것이 더 낫습니까?
Tieson T.

1
jQuery를 들어, 가치 또한 대안에 추가하고있다 $(window).load()(그리고에 가능한 구문 대안 $(document).ready(), $(function(){})? 그것은 관련된 것 같다, 그러나 느낀다 점 당신이있는 거 결정 약간 접선.
다윗은 분석 재개 모니카 말한다

@David : 좋은 지적 .load. 구문 대안에 대해서는 문서에서 찾아 볼 수 있지만 답변은 자체적으로 포함되어야하므로 실제로 추가해야합니다. 그런 다음 jQuery에 대한이 특정 비트가 이미 응답되었으며 다른 답변으로 덮여있을 수 있으며 링크가 충분할 것입니다.
Felix Kling

1
@David.load : 수정해야합니다 : api.jquery.com/load-event 는 더 이상 사용되지 않습니다 . 이미지 전용인지 아닌지 모르겠습니다 window.
Felix Kling

1
@David : 걱정 마세요 :) 확실하지 않더라도 언급 한 것이 좋습니다. 사용법을 보여주기 위해 간단한 코드 스 니펫을 추가 할 것입니다. 입력 해 주셔서 감사합니다!
Felix Kling

15

ID 기반 선택기가 작동하지 않는 이유

  1. ID가 지정된 요소 / DOM이 아직 존재하지 않습니다.
  2. 요소가 존재하지만 DOM에 등록되지 않았습니다 [Ajax 응답에서 동적으로 추가 된 HTML 노드의 경우].
  3. 동일한 ID를 가진 둘 이상의 요소가 존재하여 충돌을 일으 킵니다.

솔루션

  1. 선언 후 요소에 액세스하거나 다음과 같은 것을 사용하십시오 $(document).ready();

  2. Ajax 응답에서 오는 요소 .bind()의 경우 jQuery 메소드를 사용하십시오 . 이전 버전의 jQuery도 .live()마찬가지였습니다.

  3. 도구 (예 : 브라우저 용 웹 개발자 플러그인)를 사용하여 중복 ID를 찾아 제거하십시오.


13

@FelixKling이 지적했듯이 가장 가능성이 높은 시나리오는 찾고있는 노드가 존재하지 않는 것입니다 (아직).

그러나 현대의 개발 관행은 종종 DocumentFragments를 사용하거나 현재 요소를 직접 분리 / 다시 연결하여 문서 트리 외부의 문서 요소를 조작 할 수 있습니다. 이러한 기술은 JavaScript 템플릿의 일부로 사용되거나 해당 요소가 크게 변경되는 동안 과도한 다시 페인트 / 리플 로우 작업을 피하기 위해 사용될 수 있습니다.

마찬가지로 최신 브라우저에서 롤아웃되는 새로운 "Shadow DOM"기능을 사용하면 요소를 문서의 일부로 만들 수 있지만 document.getElementById 및 모든 형제 메서드 (querySelector 등)에서 쿼리 할 수는 없습니다. 이것은 기능을 캡슐화하고 구체적으로 숨기려고 수행됩니다.

다시 말하지만, 당신이 찾고있는 요소가 문서에 아직 (아직) 포함되어 있지 않을 가능성이 높으며 Felix가 제안한대로해야합니다. 그러나 이것이 요소를 찾을 수없는 (일시적으로 또는 영구적으로) 유일한 이유는 아니라는 점에 유의해야합니다.


13

액세스하려는 요소가 내부에 iframe있고 컨텍스트 외부에서 액세스하려고 iframe하면 실패 할 수도 있습니다.

iframe에서 요소를 얻으려면 여기 에서 방법을 찾을 수 있습니다 .


7

스크립트 실행 순서가 문제가 아닌 경우 문제의 또 다른 원인은 요소가 올바르게 선택되지 않았기 때문입니다.

  • getElementById전달 된 문자열은 ID verbatim 이어야하며 다른 것은 필요하지 않습니다. 전달 된 문자열 앞에 접두사를 붙이고 #ID가로 시작하지 않으면 #아무 것도 선택 되지 않습니다 .

    <div id="foo"></div>
    // Error, selected element will be null:
    document.getElementById('#foo')
    // Fix:
    document.getElementById('foo')
  • 마찬가지로 for 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오류가 발생합니다.

  • 이 선택기 메소드에 문자열을 전달하십시오. 당신이에 문자열이 아닌 뭔가를 전달하면 querySelector, getElementById등, 거의 확실하게 작동하지 않습니다.


0

내가 코드를 시도했을 때 효과가있었습니다.

이벤트가 작동하지 않는 유일한 이유는 DOM이 준비되지 않았고 ID가 "event-btn"인 버튼이 아직 준비되지 않았기 때문일 수 있습니다. 그리고 자바 스크립트가 실행되어 해당 요소와 이벤트를 바인딩하려고했습니다.

바인딩에 DOM 요소를 사용하기 전에 해당 요소가 준비되어 있어야합니다. 이를위한 많은 옵션이 있습니다.

옵션 1 : 문서 준비 이벤트 내에서 이벤트 바인딩 코드를 이동할 수 있습니다. 처럼:

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

옵션 2 : 시간 초과 이벤트를 사용하여 바인딩이 몇 초 동안 지연 될 수 있습니다. 처럼:

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

옵션 3 : 자바 스크립트 포함을 페이지 하단으로 이동합니다.

이것이 도움이되기를 바랍니다.


@ Willdomybest18,이 답변을 확인하십시오
Jatinder Kumar

-1

문제는 자바 스크립트 코드가 실행될 때 dom 요소 'speclist'가 생성되지 않는다는 것입니다. 그래서 자바 스크립트 코드를 함수 안에 넣고 body onload 이벤트에서 해당 함수를 호출했습니다.

function do_this_first(){
   //appending code
}

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

-1

내 솔루션은 앵커 요소의 ID를 사용하지 않는 것입니다 : <a id='star_wars'>Place to jump to</a>. 분명히 블레이저와 다른 스파 프레임 워크는 같은 페이지의 앵커로 점프하는 데 문제가 있습니다. 그 주위를 돌아 다니려면 사용해야했다 document.getElementById('star_wars'). 그러나 대신 단락 요소에 ID를 넣을 때까지 작동하지 않았습니다 <p id='star_wars'>Some paragraph<p>.

부트 스트랩 사용 예 :

<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>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.