Neden jQuery veya getElementById gibi bir DOM yöntemi öğeyi bulamıyor?


483

Ne için olası nedenler şunlardır document.getElementById, $("#id")ya da başka herhangi DOM yöntemi / jQuery seçici unsurları bulmak değil mi?

Örnek sorunlar şunları içerir:

  • jQuery sessizce bir olay işleyicisini bağlayamıyor
  • jQuery "alıcı" yöntemleri ( .val(), .html(), .text()) dönenundefined
  • nullBirkaç hatayla sonuçlanan standart bir DOM yöntemi döndürülür :

Yakalanmamış TypeError: null özelliği '...' ayarlanamıyor Yakalanmamış TypeError: null özelliği '...' okunamıyor

En yaygın formlar:

Yakalanmayan TypeError: 'onclick' özelliği null olarak ayarlanamıyor

Yakalanmayan TypeError: null 'addEventListener' özelliği okunamıyor

Yakalanmayan TypeError: null özelliği 'style' özelliği okunamıyor


32
Belirli bir DOM öğesinin neden bulunmadığı ve bunun nedeni genellikle JavaScript kodunun DOM öğesinden önce yerleştirilmesiyle ilgili birçok soru sorulur. Bu, bu tür sorular için kanonik bir cevap olarak düşünülmüştür. Topluluk wiki'si, bu yüzden lütfen onu geliştirmekten çekinmeyin .
Felix Kling

Yanıtlar:


481

Find çalıştığınız eleman değildi DOM sizin komut koştu.

DOM'a bağlı komut dosyanızın konumu, davranışı üzerinde derin bir etkiye sahip olabilir. Tarayıcılar HTML belgelerini yukarıdan aşağıya ayrıştırır. Öğeler DOM'a eklenir ve komut dosyaları (genellikle) karşılaşıldıklarında yürütülür. Bu, siparişin önemli olduğu anlamına gelir. Genellikle, komut dosyaları biçimlendirmede daha sonra görünen öğeleri bulamaz, çünkü bu öğelerin henüz DOM'a eklenmesi gerekmez.

Aşağıdaki işaretlemeyi düşünün; # 1 <div>betiği, 2 numaralı betiğin başarılı olduğunu bulamıyor :

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

Peki ne yapmalısın? Birkaç seçeneğiniz var:


1. Seçenek: Betiğinizi taşıyın

Komut dosyanızı, kapanış gövdesi etiketinden hemen önce, sayfanın altına taşıyın. Bu şekilde düzenlenen belgenin geri kalanı, komut dosyanız yürütülmeden ayrıştırılır:

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

Not: Betiklerin altına yerleştirilmesi genellikle en iyi uygulama olarak kabul edilir .


Seçenek 2: jQuery's ready()

Komut dosyanızı DOM aşağıdakiler kullanılarak tamamen ayrıştırılana kadar erteleyin :$(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>

Not: Sadece bağlanabilir DOMContentLoadedveya her birinin kendi uyarıları vardır. jQuery's hibrit bir çözüm sunar.window.onloadready()


3. Seçenek: Etkinlik Temsilcisi

Yetki verilen olayların avantajı, daha sonra belgeye eklenen alt öğelerin olaylarını işleyebilmesidir.

Bir öğe bir olayı oluşturduğunda ( köpüren bir olay olması ve hiçbir şeyin yayılmasını durdurmaması şartıyla ), o öğenin soyundaki her ebeveyn de olayı alır. Bu, var olan bir elemana bir işleyici eklememize ve torunları eklendikten sonra eklenenler bile olayları örneklemesine olanak tanıyor. Tek yapmamız gereken olayı istenen eleman tarafından yükseltilip yükseltilmediğini kontrol etmek ve eğer öyleyse kodumuzu çalıştırmak.

jQuery's on()bu mantığı bizim için gerçekleştirir. Sadece bir olay adı, istenen torun için bir seçici ve bir olay işleyicisi sağlıyoruz:

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

Not: Tipik olarak, bu desen yükleme sırasında var olmayan elemanlar veya çok sayıda işleyici takmaktan kaçınmak için ayrılmıştır . Ayrıca document(gösterme amacıyla) bir işleyici bağlarken en yakın güvenilir atayı seçmeniz gerektiğini belirtmek gerekir.


Seçenek 4: deferÖzellik

Öğesinin deferözelliğini kullanın <script>.

[ defer, bir Boolean özniteliği], bir tarayıcıya komut dosyasının belge ayrıştırıldıktan sonra ancak tetiklemeden önce yürütülmesi gerektiğini belirtecek şekilde ayarlanmıştır DOMContentLoaded.

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

Başvuru için, bu harici komut dosyasından kod :

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

Not: deferÖzellik kesinlikle sihirli bir mermi gibi görünüyor, ancak uyarıların farkında olmak önemlidir ...
1. defersadece harici komut dosyaları için kullanılabilir, yani: bir srcniteliğe sahip olanlar .
2. tarayıcı desteğinin farkında olun , yani: IE'de buggy uygulaması <10


Şimdi 2020 - "Altyazıları yerleştirmek" hala "en iyi uygulama" olarak kabul ediliyor mu? Tüm kaynaklarımı içine koydum <head>ve deferkomut dosyalarında kullanıyorum (kötü eski uyumsuz tarayıcıları desteklememe gerek yok)
Stephen P

Modern tarayıcılara geçmenin büyük bir savunucusuyum. Bununla birlikte, bu uygulamanın bana hiçbir maliyeti yok ve sadece her yerde çalışıyor. Öte yandan, her ikisi de deferve asyncoldukça geniş bir destek var -bir çok eski tarayıcı sürümlerine geri ulaşan. Amaçlanan davranışı açıkça, açıkça işaret etmenin ek avantajına sahiptirler. Bu niteliklerin yalnızca bir srcniteliği belirten komut dosyalarında , yani harici komut dosyalarında çalıştığını unutmayın . Avantajları tartın. Belki ikisini de kullanabilirsin? Çağrınız.
canon

146

Kısa ve basit: Çünkü aradığınız öğeler belgede mevcut değil (henüz).


Bu yanıt geri kalanı için kullanmak olacaktır getElementByIdörnek olarak, aynı için de geçerlidir getElementsByTagName, querySelectorve diğer herhangi bir DOM yöntemi seçer elemanlarının.

Olası nedenler

Bir öğenin var olmamasının iki nedeni vardır:

  1. İletilen kimliğe sahip bir öğe belgede gerçekten yok. İletdiğiniz getElementByIdkimliğin (oluşturulan) HTML'deki mevcut bir öğenin kimliğiyle gerçekten eşleşip eşleşmediğini ve kimliği yanlış yazdığınızdan (kimlikler büyük / küçük harfe duyarlıdır !) İki kez kontrol etmelisiniz .

    Bu arada, uygulayan ve yöntem yapan çağdaş tarayıcıların çoğunda , CSS tarzı gösterim, bir öğeyi , örneğin: bir öğenin altından alma yönteminin aksine, bir öğeyi almak için kullanılır ; birincisinde karakter esastır, ikincisinde elemanın alınmamasına yol açar.querySelector()querySelectorAll()iddocument.querySelector('#elementID')iddocument.getElementById('elementID')#

  2. Öğe, aradığınız anda mevcut değil getElementById.

İkinci durum oldukça yaygındır. Tarayıcılar HTML'yi yukarıdan aşağıya ayrıştırır ve işler. Bu, söz konusu DOM öğesinin HTML'de görünmesinden önce gerçekleşen bir DOM öğesine yapılan tüm çağrıların başarısız olacağı anlamına gelir.

Aşağıdaki örneği düşünün:

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

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

divGörünür sonrascript . Komut dosyası yürütüldüğünde, öğe henüz mevcut değildir ve getElementByIdgeri döner null.

jQuery

Aynı durum jQuery'li tüm seçiciler için de geçerlidir. jQuery, seçicinizi yanlış yazdıysanız veya gerçekte var olmadan önce seçmeye çalışıyorsanız öğeleri bulamaz .

Eklenen bir bükülme, komut dosyasını protokol olmadan yüklediğiniz ve dosya sisteminden çalıştırdığınız için jQuery bulunamadığında ortaya çıkar:

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

bu sözdizimi, komut dosyasının https: // içeren bir sayfaya HTTPS aracılığıyla yüklenmesine ve http: // protokolüne sahip bir sayfaya HTTP sürümünü yüklemek için kullanılır.

Yükleme denemesinin ve başarısız olmasının talihsiz yan etkisi vardır. file://somecdn.somewhere.com...


Çözümler

Bir çağrı yapmadan önce getElementById(veya bu konuyla ilgili herhangi bir DOM yönteminden), erişmek istediğiniz öğelerin, yani DOM'un yüklendiğinden emin olun.

Bu , ilgili DOM öğesinden sonra JavaScript'inizi yerleştirerek sağlanabilir

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

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

bu durumda kodu, body kodunun kapanışından hemen önce koyabilirsiniz ( </body>) (tüm DOM öğeleri komut dosyası yürütülürken kullanılabilir olacaktır).

Diğer çözümler arasında load [MDN] veya DOMContentLoaded [MDN] olaylarını dinleme sayılabilir . Bu durumlarda, belgede JavaScript kodunu nereye yerleştirdiğiniz önemli değildir, tüm DOM işleme kodunu olay işleyicilerine koymayı hatırlamanız yeterlidir.

Misal:

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

// or

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

Olay işleme ve tarayıcı farklılıkları hakkında daha fazla bilgi için lütfen quirksmode.org'daki makalelere bakın .

jQuery

Önce jQuery'nin düzgün yüklendiğinden emin olun. JQuery dosyasının bulunup bulunmadığını öğrenmek için tarayıcının geliştirici araçlarını kullanın ve bulunmuyorsa URL'yi düzeltin (ör . Başlangıçta http:veya https:şemasını ekleyin , yolu ayarlayın, vb.)

load/ DOMContentLoaded Olaylarını dinlemek jQuery'nin .ready() [docs] ile yaptığı şeydir . DOM öğesini etkileyen tüm jQuery kodunuz bu olay işleyicinin içinde olmalıdır.

Aslında, jQuery öğreticisi açıkça şunları belirtir:

JQuery kullanırken yaptığımız hemen hemen her şey belge nesne modelini (DOM) okur veya manipüle ederken, DOM hazır olur olmaz etkinlik vb. Eklemeye başladığımızdan emin olmamız gerekir.

Bunu yapmak için, belge için hazır bir olay kaydederiz.

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

Alternatif olarak steno sözdizimini de kullanabilirsiniz:

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

Her ikisi de eşdeğerdir.


1
ready"Yeni" tercih edilen sözdizimini yansıtmak için etkinlikle ilgili son kısmı değiştirmeye değer mi yoksa eski sürümlerde çalışacak şekilde olduğu gibi bırakmak daha mı iyi?
Tieson T.

1
JQuery için, buna değer de alternatif ekleyerek olduğu $(window).load()(ve muhtemelen sözdizimsel alternatifleri $(document).ready(), $(function(){})? Bunlar arasında bir ilişki görünüyor, ama onu hissediyor noktaya sen yapma biraz teğet.
David yeniden aktifleştirme Monica diyor

@David: İyi bir noktaya değindin .load. Sözdizimi alternatifleriyle ilgili olarak, dokümantasyona bakılabilirler, ancak cevapların kendi içinde olması gerektiğinden, aslında onları eklemeye değer olabilir. Sonra tekrar, jQuery hakkında bu belirli bit zaten cevap ve muhtemelen başka bir cevap kapsamında ve emin olabilirim emin olabilirim eminim.
Felix Kling

1
@David: Gözden geçirmem gerekiyor: Görünüşe göre .loadkullanımdan kaldırıldı: api.jquery.com/load-event . Bunun sadece görüntüler için mi yoksa windowaynı zamanda mı olduğunu bilmiyorum .
Felix Kling

1
@David: Endişelenmeyin :) Emin olmasanız bile, bahsetmeniz iyi oldu. Muhtemelen kullanımı göstermek için sadece bazı basit kod parçacıkları ekleyeceğim. Girdiniz için teşekkürler!
Felix Kling

15

Kimlik tabanlı seçicilerin çalışma nedenleri

  1. Belirtilen kimliğe sahip DOM öğesi henüz mevcut değil.
  2. Öğe var, ancak DOM'da kayıtlı değil [Ajax yanıtlarından dinamik olarak eklenen HTML düğümleri olması durumunda].
  3. Aynı kimliğe sahip, çakışmaya neden olan birden fazla öğe var.

Çözümler

  1. Bildirgeden sonra öğeye erişmeyi deneyin veya alternatif olarak $(document).ready();

  2. Ajax yanıtlarından gelen öğeler için .bind()jQuery yöntemini kullanın . Eski jQuery sürümleri .live()de aynıydı.

  3. Yinelenen kimlikleri bulmak ve kaldırmak için araçlar (örneğin, tarayıcılar için web geliştirici eklentisi) kullanın.


13

@FelixKling'in işaret ettiği gibi, en olası senaryo, aradığınız düğümlerin (henüz) mevcut olmamasıdır.

Bununla birlikte, modern geliştirme uygulamaları genellikle DocumentFragments ile belge öğelerinin dışındaki belge öğelerini değiştirebilir veya yalnızca geçerli öğeleri doğrudan ayırabilir / yeniden bağlayabilir. Bu teknikler, JavaScript şablonlamasının bir parçası olarak veya söz konusu öğeler büyük ölçüde değiştirilirken aşırı boyama / yeniden akış işlemlerinden kaçınmak için kullanılabilir.

Benzer şekilde, modern tarayıcılarda sunulan yeni "Gölge DOM" işlevselliği, öğelerin belgenin bir parçası olmasına izin verir, ancak document.getElementById ve tüm kardeş yöntemleri (querySelector vb.) Tarafından sorgulanamaz. Bu, işlevselliği kapsüllemek ve özellikle gizlemek için yapılır.

Yine de, büyük olasılıkla aradığınız öğenin belgede (henüz) olmaması ve Felix'in önerdiği gibi yapmanız gerekir. Bununla birlikte, bir öğenin uygunsuz olmasının (geçici veya kalıcı olarak) tek sebebinin giderek artan bir şekilde bu olmadığının farkında olmalısınız.


13

Erişmeye çalıştığınız öğe bir içindeyse iframeve içeriğine erişmeye çalıştığınızda iframebunun da başarısız olmasına neden olur.

Bir iframe'de bir öğe almak istiyorsanız, nasıl yapılacağını buradan öğrenebilirsiniz .


7

Komut dosyası yürütme sırası sorun değilse, sorunun başka bir olası nedeni öğenin düzgün seçilmemesidir:

  • getElementByIdiletilen dizenin ID kelimesi olmasını gerektirir ve başka bir şey olmaz. Geçirilen dizeye a önekini eklerseniz #ve kimlik bir ile başlamazsa #hiçbir şey seçilmez:

    <div id="foo"></div>
    // Error, selected element will be null:
    document.getElementById('#foo')
    // Fix:
    document.getElementById('foo')
  • Benzer şekilde, için getElementsByClassName, iletilen dizenin önüne bir önek eklemeyin .:

    <div class="bar"></div>
    // Error, selected element will be undefined:
    document.getElementsByClassName('.bar')[0]
    // Fix:
    document.getElementsByClassName('bar')[0]
  • QuerySelector, querySelectorAll ve jQuery ile bir öğeyi belirli bir sınıf adıyla eşleştirmek için .doğrudan sınıfın önüne yerleştirin. Benzer şekilde, bir öğeyi belirli bir kimlikle eşleştirmek için #doğrudan kimliğin önüne yerleştirin:

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

    Buradaki kurallar, çoğu durumda, CSS seçicileriyle aynıdır ve burada ayrıntılı olarak görülebilir .

  • İki veya daha fazla özniteliğe sahip bir öğeyi eşleştirmek için (iki sınıf adı veya bir sınıf adı ve data-öznitelik gibi), her bir öznitelik için seçicileri, birbirinden ayıran bir boşluk olmadan , seçim dizesinde yan yana koyun (boşluk belirttiği için) soyundan seçici ). Örneğin, seçmek için:

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

    sorgu dizesini kullanın .foo.bar. Seçmek

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

    sorgu dizesini kullanın .foo[data-bar="someData"]. <span>Aşağıdakileri seçmek için :

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

    kullanın div.parent > span[data-username="bob"].

  • Büyük harf kullanımı ve yazım yukarıdakilerin tümü için önemlidir . Büyük / küçük harf farklıysa veya yazım farklıysa, öğe seçilmez:

    <div class="result"></div>
    // Error, selected element will be null:
    document.querySelector('.results')
    $('.Result')
    // Fix:
    document.querySelector('.result')
    $('.result')
  • Ayrıca, yöntemlerin doğru büyük harf ve yazım kurallarına sahip olduğundan emin olmanız gerekir. Şunlardan birini kullanın:

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

    Başka hiçbir yazım veya büyük harf kullanımı işe yaramaz. Örneğin, document.getElementByClassNamebir hata verir.

  • Bu seçici yöntemlere bir dize ilettiğinizden emin olun. Eğer bir dize olmayan bir şey geçerseniz querySelector, getElementByIdvb, hemen hemen kesinlikle iş olmaz.


0

Kodunuzu denediğimde işe yaradı.

Etkinliğinizin çalışmamasının tek nedeni DOM'nizin hazır olmaması ve "event-btn" kimliğine sahip düğmenizin henüz hazır olmaması olabilir. Ve javascriptiniz çalıştırıldı ve etkinliği bu öğeyle bağlamaya çalıştı.

DOM öğesini ciltleme için kullanmadan önce, bu öğenin hazır olması gerekir. Bunu yapmak için birçok seçenek var.

Seçenek1: Olay bağlama kodunuzu belgeye hazır olay içinde taşıyabilirsiniz. Sevmek:

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

Seçenek2: Zaman aşımı olayını kullanarak bağlamanın birkaç saniye gecikmesini sağlayabilirsiniz. sevmek:

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

Seçenek3: Javascript içerme sayfanızı altına taşıyın.

Umarım bu sana yardımcı olur.


@ Willdomybest18, lütfen bu cevabı kontrol et
Jatinder Kumar

-1

sorun, javascript kodunun yürütüldüğü sırada 'speclist' dom öğesinin oluşturulmamasıdır. Javascript kodunu bir fonksiyonun içine koydum ve body onload olayında bu fonksiyonu çağırdım.

function do_this_first(){
   //appending code
}

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

-1

Benim çözümüm Bir çapa elemanının kimliği kullanmıyor oldu: <a id='star_wars'>Place to jump to</a>. Görünüşe göre blazor ve diğer spa çerçeveleri aynı sayfada bağlantılara atlamakta sorun yaşıyor. Bunu yapmak için kullanmak zorundaydım document.getElementById('star_wars'). Bunun yerine bir paragraf öğesi kimliği koymak kadar Ancak bu işe yaramadı: <p id='star_wars'>Some paragraph<p>.

Bootstrap kullanan örnek:

<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>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.