Bagaimana cara memeriksa Javascript jika satu elemen terkandung dalam elemen lainnya


202

Bagaimana saya bisa memeriksa apakah satu elemen DOM adalah anak dari elemen DOM lainnya? Apakah ada metode bawaan untuk ini? Misalnya, sesuatu seperti:

if (element1.hasDescendant(element2)) 

atau

if (element2.hasParent(element1)) 

Jika tidak maka ada ide bagaimana melakukan ini? Itu juga harus browser lintas. Saya juga harus menyebutkan bahwa anak itu bisa bersarang banyak tingkat di bawah orang tua.


3
Anda harus menggunakan stackoverflow.com/a/18234150/1868545
llange

Saya percaya bahwa karena JS saat ini merupakan "standar langsung", jawaban yang diterima harus diperiksa ulang untuk menyatakan metode "conatin" sebagai jawaban yang diterima.
DavidTaubmann

Jawaban:


219

Pembaruan: Sekarang ada cara asli untuk mencapai ini. Node.contains(). Disebutkan dalam komentar dan jawaban di bawah ini juga.

Jawaban lama:

Penggunaan parentNodeproperti harus berhasil. Ini juga cukup aman dari sudut pandang lintas-browser. Jika hubungannya dikenal sedalam satu level, Anda bisa memeriksanya hanya:

if (element2.parentNode == element1) { ... }

Jika anak tersebut dapat bersarang secara acak jauh di dalam orang tua, Anda dapat menggunakan fungsi yang mirip dengan yang berikut untuk menguji hubungan:

function isDescendant(parent, child) {
     var node = child.parentNode;
     while (node != null) {
         if (node == parent) {
             return true;
         }
         node = node.parentNode;
     }
     return false;
}

Terima kasih atas balasan Anda, masalahnya adalah bahwa anak itu dapat bersarang banyak tingkat di bawah orangtua.
AJ.

@AJ: Saya memperbarui jawaban saya untuk memasukkan solusi yang seharusnya berfungsi untuk menyarangkan anak secara sewenang-wenang jauh di dalam orangtua.
Asaf

228
Jika ada yang datang ke ini sekarang mungkin Anda dapat menggunakan Node.contains ( developer.mozilla.org/en-US/docs/DOM/Node.contains ) yang merupakan fungsi asli di peramban modern.
Adam Heath

2
@JohnCarrell Ada Node.contains(tidak ada halaman caniuse sekalipun)
gcampbell

3
@ Asaf Bisakah Anda memperbarui jawaban Anda untuk memasukkan yang baru Node.contains? :)

355

Anda harus menggunakan Node.contains, karena sekarang standar dan tersedia di semua browser.

https://developer.mozilla.org/en-US/docs/Web/API/Node.contains


Metode Node.hasParent (induk) tidak perlu tetapi akan menjadi Node.prototype.hasParent = fungsi (elemen) {return element.contains (this);};
llange

6
Apakah ini juga didukung di browser seluler? Dokumen mdn memiliki tanda tanya untuk Android, Safari, IE, dan Opera.
minggu kedua

1
@thetallweeks - Paling tidak berfungsi di Android 7 saya.
Sphinxxx

5
Anda setidaknya bisa menambahkan contoh
Nino Škopac

2
Ini adalah jawaban yang baik tetapi contohnya akan sangat menyenangkan:var parent = document.getElementById('menu'); var allElements = document.getElementsByTagName('a'); if (parent.contains(allElements[i]) { alert('Link is inside meni'); }
baron_bartek

45

Saya hanya harus berbagi 'milik saya'.

Meskipun secara konseptual sama dengan jawaban Asaph (mendapat manfaat dari kompatibilitas lintas-browser yang sama, bahkan IE6), itu jauh lebih kecil dan berguna ketika ukurannya premium dan / atau ketika tidak sering dibutuhkan.

function childOf(/*child node*/c, /*parent node*/p){ //returns boolean
  while((c=c.parentNode)&&c!==p); 
  return !!c; 
}

..atau satu baris ( hanya 64 karakter !):

function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}

dan jsfiddle di sini .


Penggunaan:
childOf(child, parent) mengembalikan booleantrue| false.

Penjelasan:
while mengevaluasi selama kondisi sementara mengevaluasitrue.
Operator&&(DAN) mengembalikan boolean ini benar / salah setelah mengevaluasi sisi kiri dan sisi kanan, tetapi hanya jika sisi kiri benar ( left-hand && right-hand) .

Sisi kiri (dari &&) adalah: (c=c.parentNode).
Ini pertama-tama akan menetapkan parentNodeof cto cdan kemudian operator AND akan mengevaluasi hasilnya csebagai boolean.
Sejak parentNodekembali nulljika tidak ada induk yang tersisa dan nulldikonversi ke false, loop-sementara akan berhenti dengan benar ketika tidak ada lagi orangtua.

Sisi kanan (dari &&) adalah: c!==p.
The !==operator perbandingan adalah ' tidak persis sama untuk'. Jadi jika orang tua anak bukan orang tua (yang Anda tentukan) dievaluasi true, tetapi jika orang tua anak adalah orang tua maka dievaluasi untuk false.
Jadi, jika c!==p bernilai false, maka &&operator kembali falseketika kondisi-sementara dan loop-sementara berhenti. (Perhatikan bahwa tidak diperlukan tubuh sementara dan ;titik koma penutup diperlukan.)

Jadi ketika loop-sementara berakhir, cadalah simpul (bukan null) ketika ia menemukan orangtua ATAU itu null(ketika loop berlari ke ujung tanpa menemukan kecocokan).

Jadi kita hanya returnfakta (dikonversi sebagai nilai boolean, bukan node) dengan: return !!c;: yang !( NOToperator) membalikkan nilai boolean ( truemenjadi falsedan sebaliknya).
!cmengkonversi c(node ​​atau null) ke boolean sebelum dapat membalikkan nilai itu. Jadi menambahkan kedua !( !!c) mengkonversi palsu ini kembali ke true (yang mengapa ganda !!sering digunakan untuk 'convert apapun untuk boolean').


Ekstra:
Badan / payload fungsi sangat kecil sehingga, tergantung pada kasus (seperti ketika itu tidak sering digunakan dan muncul hanya sekali dalam kode), seseorang bahkan bisa menghilangkan fungsi (membungkus) dan hanya menggunakan loop sementara:

var a=document.getElementById('child'),
    b=document.getElementById('parent'),
    c;

c=a; while((c=c.parentNode)&&c!==b); //c=!!c;

if(!!c){ //`if(c)` if `c=!!c;` was used after while-loop above
    //do stuff
}

dari pada:

var a=document.getElementById('child'),
    b=document.getElementById('parent'),
    c;

function childOf(c,p){while((c=c.parentNode)&&c!==p);return !!c}

c=childOf(a, b);    

if(c){ 
    //do stuff
}

4
@SolomonUcko: Perbandingan kesetaraan yang ketat ( !==) dapat meningkatkan kecepatan dengan memperjelas kepada kompiler bahwa ia dapat melewati langkah-langkah pengecekan tipe dan opsional yang tersirat yang akan terjadi dalam perbandingan kesetaraan yang longgar, sehingga meningkatkan kecepatan (dengan lebih akurat menggambarkan apa yang kami ingin terjadi, yang juga bermanfaat bagi programmer). Saya juga ingat ketika menulis ini, bahwa hanya perbandingan longgar ( !=) yang sangat rawan kesalahan atau tidak berfungsi sama sekali di beberapa browser yang lebih lama (saya menduga itu ada di IE6, tapi saya lupa ).
GitaarLAB

29

Solusi lain yang tidak disebutkan:

Contoh Di Sini

var parent = document.querySelector('.parent');

if (parent.querySelector('.child') !== null) {
    // .. it's a child
}

Tidak masalah apakah elemen tersebut adalah anak langsung, itu akan bekerja pada kedalaman apa pun.


Atau, gunakan .contains()metode ini :

Contoh Di Sini

var parent = document.querySelector('.parent'),
    child = document.querySelector('.child');

if (parent.contains(child)) {
    // .. it's a child
}

19

Lihatlah Node # compareDocumentPosition .

function isDescendant(ancestor,descendant){
    return ancestor.compareDocumentPosition(descendant) & 
        Node.DOCUMENT_POSITION_CONTAINS;
}

function isAncestor(descendant,ancestor){
    return descendant.compareDocumentPosition(ancestor) & 
        Node.DOCUMENT_POSITION_CONTAINED_BY;
}

Hubungan lain termasuk DOCUMENT_POSITION_DISCONNECTED, DOCUMENT_POSITION_PRECEDING, dan DOCUMENT_POSITION_FOLLOWING.

Tidak didukung di IE <= 8.


menarik! Ini mengingatkan saya pada fungsi JS yang saya kembangkan pada tahun 2003 ... Saya butuh beberapa hari dan mendapat bantuan dari beberapa pengembang JS ... Luar biasa (dan menyedihkan) bahwa saat ini masih belum ada implementasi browser penuh drag-drop atau objek-fit penuh .
DavidTaubmann


2

Saya menemukan potongan kode yang bagus untuk memeriksa apakah suatu elemen adalah anak dari elemen lain atau tidak. Saya harus menggunakan ini karena IE tidak mendukung.contains metode elemen. Semoga ini bisa membantu orang lain juga.

Di bawah ini fungsinya:

function isChildOf(childObject, containerObject) {
  var returnValue = false;
  var currentObject;

  if (typeof containerObject === 'string') {
    containerObject = document.getElementById(containerObject);
  }
  if (typeof childObject === 'string') {
    childObject = document.getElementById(childObject);
  }

  currentObject = childObject.parentNode;

  while (currentObject !== undefined) {
    if (currentObject === document.body) {
      break;
    }

    if (currentObject.id == containerObject.id) {
      returnValue = true;
      break;
    }

    // Move up the hierarchy
    currentObject = currentObject.parentNode;
  }

  return returnValue;
}

Saya benar-benar mencoba setiap polyfill lain di halaman ini termasuk .contains, dan ini adalah satu-satunya yang berfungsi. Terima kasih!
protoEvangelion

1

coba yang ini:

x = document.getElementById("td35");
if (x.childElementCount > 0) {
    x = document.getElementById("LastRow");
    x.style.display = "block";
}
else {
    x = document.getElementById("LastRow");
    x.style.display = "none";
}

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.