Javascript .querySelector menemukan <div> oleh innerTEXT

109

Bagaimana cara menemukan DIV dengan teks tertentu? Sebagai contoh:

<div>
SomeText, text continues.
</div>

Mencoba menggunakan sesuatu seperti ini:

var text = document.querySelector('div[SomeText*]').innerTEXT;
alert(text);

Tapi tentu saja itu tidak akan berhasil. Bagaimana saya bisa melakukannya?

passwd
sumber
Bahkan jika Anda bisa melakukannya, itu tidak akan lebih cepat daripada mendapatkan semua div dan memfilternya melalui properti innerText. Jadi mengapa Anda tidak melakukannya secara manual.
Redu

Jawaban:

100

Pertanyaan OP adalah tentang JavaScript biasa dan bukan jQuery . Meskipun ada banyak jawaban dan saya suka jawaban @Pawan Nogariya , silakan lihat alternatif ini.

Anda dapat menggunakan XPATH di JavaScript. Info lebih lanjut tentang artikel MDN di sini .

The document.evaluate()Metode mengevaluasi sebuah XPath permintaan / ekspresi. Jadi Anda bisa melewatkan ekspresi XPATH di sana, melintasi ke dalam dokumen HTML dan menemukan elemen yang diinginkan.

Di XPATH Anda dapat memilih elemen, dengan simpul teks seperti berikut, yang mendapatkan divsimpul teks berikut.

//div[text()="Hello World"]

Untuk mendapatkan elemen yang berisi beberapa teks, gunakan yang berikut ini:

//div[contains(., 'Hello')]

The contains()metode dalam XPath mengambil simpul sebagai parameter pertama dan teks untuk mencari sebagai parameter kedua.

Periksa plunk ini di sini , ini adalah contoh penggunaan XPATH di JavaScript

Berikut ini cuplikan kode:

var headings = document.evaluate("//h1[contains(., 'Hello')]", document, null, XPathResult.ANY_TYPE, null );
var thisHeading = headings.iterateNext();

console.log(thisHeading); // Prints the html element in console
console.log(thisHeading.textContent); // prints the text content in console

thisHeading.innerHTML += "<br />Modified contents";  

Seperti yang Anda lihat, saya dapat mengambil elemen HTML dan memodifikasinya sesuka saya.

gdyrrahitis
sumber
Terima kasih! Bekerja dengan baik! Tetapi bagaimana cara "console.log" ke "thisHeading.textContent" jika saya hanya perlu mengambil satu kata dari teks ini? Misalnya: '// div [berisi (., \' / Anda login (. *) Kali sesi ini / \ ')]' dan kemudian peringatan (thisHeading.textContent. $ 1)
passwd
Oke, saya melakukannya dengan cara ini:alert(thisHeading.textContent.replace(/.*You have login (.*) times.*/,'$1')) ;
passwd
@passwd, Anda tidak bisa melakukan itu. Regex tidak didukung di XPATH 1.0 (yang .evaluate()menggunakan. Mohon koreksi seseorang jika saya salah), jadi pertama-tama, Anda tidak dapat mencari sesuatu yang cocok dengan ekspresi reguler. Kedua, .textContentproperti mengembalikan simpul teks dari elemen. Jika Anda ingin mengambil nilai dari teks ini, Anda harus menanganinya secara eksplisit, mungkin dengan membuat semacam fungsi yang cocok dengan regex dan mengembalikan nilai yang cocok dalam grup. Untuk itu buat pertanyaan baru di utas terpisah.
gdyrrahitis
Internet Explorer: Tidak ada dukungan. Tetapi didukung di Edge. Saya tidak yakin apa artinya, menurut versi.
Rolf
bagaimana seharusnya menangani kesalahan jika elemen yang saya cari hilang?
nenito
72

Anda dapat menggunakan solusi yang cukup sederhana ini:

Array.from(document.querySelectorAll('div'))
  .find(el => el.textContent === 'SomeText, text continues.');
  1. Ini Array.fromakan mengonversi NodeList menjadi array (ada beberapa metode untuk melakukan ini seperti operator penyebaran atau irisan)

  2. Hasilnya sekarang menjadi sebuah array yang memungkinkan untuk menggunakan Array.findmetode ini, Anda kemudian dapat memasukkan predikat apa pun. Anda juga bisa memeriksa textContent dengan regex atau apapun yang Anda suka.

Perhatikan bahwa Array.fromdan Array.findmerupakan fitur ES2015. Ini kompatibel dengan browser lama seperti IE10 tanpa transpiler:

Array.prototype.slice.call(document.querySelectorAll('div'))
  .filter(function (el) {
    return el.textContent === 'SomeText, text continues.'
  })[0];
Niels
sumber
2
Jika Anda ingin menemukan banyak elemen, ganti finddengan filter.
RubbelDieKatz
38

Karena Anda telah memintanya dalam javascript sehingga Anda dapat memiliki sesuatu seperti ini

function contains(selector, text) {
  var elements = document.querySelectorAll(selector);
  return Array.prototype.filter.call(elements, function(element){
    return RegExp(text).test(element.textContent);
  });
}

Dan kemudian menyebutnya seperti ini

contains('div', 'sometext'); // find "div" that contain "sometext"
contains('div', /^sometext/); // find "div" that start with "sometext"
contains('div', /sometext$/i); // find "div" that end with "sometext", case-insensitive
Pawan Nogariya
sumber
1
Sepertinya ini berfungsi, tetapi sebagai gantinya saya hanya mendapatkan ini:[object HTMLDivElement],[object HTMLDivElement]
passwd
Ya, Anda akan mendapatkan div dengan teks yang cocok di dalamnya dan kemudian Anda dapat menyebutnya metode teks bagian dalam seperti ini foundDivs[0].innerText, sesederhana itu
Pawan Nogariya
20

Solusi ini melakukan hal berikut:

  • Menggunakan operator sebaran ES6 untuk mengonversi NodeList semua divs menjadi larik.

  • Memberikan output jika div berisi string kueri, tidak hanya jika sama persis dengan string kueri (yang terjadi untuk beberapa jawaban lain). misalnya Ia harus memberikan keluaran tidak hanya untuk 'SomeText' tetapi juga untuk 'SomeText, teks berlanjut'.

  • Menghasilkan seluruh divkonten, bukan hanya string kueri. misalnya Untuk 'SomeText, teks berlanjut' itu harus menampilkan seluruh string, bukan hanya 'SomeText'.

  • Memungkinkan beberapa divs untuk memuat string, bukan hanya satu div.

[...document.querySelectorAll('div')]      // get all the divs in an array
  .map(div => div.innerHTML)               // get their contents
  .filter(txt => txt.includes('SomeText')) // keep only those containing the query
  .forEach(txt => console.log(txt));       // output the entire contents of those
<div>SomeText, text continues.</div>
<div>Not in this div.</div>
<div>Here is more SomeText.</div>

Andrew Willems
sumber
3
Aku suka ini. Bersih, ringkas, dan mudah dipahami - semuanya pada waktu yang sama.
ba_ul
2
Sangat tidak efisien tentunya? Pikirkan seberapa besar ukuran innerHTMLtop Anda <div>. Anda harus memfilter divs yang berisi turunan terlebih dahulu. Juga curiga document.getElementsByTagName('div')mungkin lebih cepat tapi saya akan patokan untuk memastikannya.
Timmmm
Ini bagus buat saya, saya bisa setel selektor yang bagus di awal karena saya sudah tahu itu hanya bisa di meja, keren, terima kasih
gsalgadotoledo
10

Anda sebaiknya melihat apakah Anda memiliki elemen induk dari div yang Anda kueri. Jika demikian, dapatkan elemen induk dan lakukan element.querySelectorAll("div"). Setelah Anda nodeListmenerapkan filter di atasnya di atas innerTextproperti. Asumsikan bahwa elemen induk dari div yang kita kueri memiliki iddari container. Anda biasanya dapat mengakses container langsung dari id tetapi mari kita lakukan dengan cara yang benar.

var conty = document.getElementById("container"),
     divs = conty.querySelectorAll("div"),
    myDiv = [...divs].filter(e => e.innerText == "SomeText");

Jadi begitu.

Redu
sumber
Ini bekerja untuk saya tetapi dengan innerHTML dan bukan innerText
Chase Sandmann
5

Jika Anda tidak ingin menggunakan jquery atau semacamnya, Anda dapat mencoba ini:

function findByText(rootElement, text){
    var filter = {
        acceptNode: function(node){
            // look for nodes that are text_nodes and include the following string.
            if(node.nodeType === document.TEXT_NODE && node.nodeValue.includes(text)){
                 return NodeFilter.FILTER_ACCEPT;
            }
            return NodeFilter.FILTER_REJECT;
        }
    }
    var nodes = [];
    var walker = document.createTreeWalker(rootElement, NodeFilter.SHOW_TEXT, filter, false);
    while(walker.nextNode()){
       //give me the element containing the node
       nodes.push(walker.currentNode.parentNode);
    }
    return nodes;
}

//call it like
var nodes = findByText(document.body,'SomeText');
//then do what you will with nodes[];
for(var i = 0; i < nodes.length; i++){ 
    //do something with nodes[i]
} 

Setelah Anda memiliki node dalam array yang berisi teks, Anda dapat melakukan sesuatu dengannya. Seperti mengingatkan masing-masing atau mencetak ke konsol. Satu peringatan adalah bahwa ini belum tentu mengambil div sendiri, ini akan mengambil induk dari textnode yang memiliki teks yang Anda cari.

Steve Botello
sumber
3

Karena tidak ada batasan panjang teks dalam atribut data, gunakan atribut data! Dan kemudian Anda dapat menggunakan pemilih css biasa untuk memilih elemen Anda seperti yang diinginkan OP.

for (const element of document.querySelectorAll("*")) {
  element.dataset.myInnerText = element.innerText;
}

document.querySelector("*[data-my-inner-text='Different text.']").style.color="blue";
<div>SomeText, text continues.</div>
<div>Different text.</div>

Idealnya Anda melakukan bagian pengaturan atribut data pada pemuatan dokumen dan mempersempit pemilih querySelectorAll sedikit untuk kinerja.

peta kunci
sumber
2

Google memiliki ini sebagai hasil teratas untuk mereka yang perlu menemukan node dengan teks tertentu. Melalui pembaruan, nodelist sekarang dapat di-iterable di browser modern tanpa harus mengubahnya menjadi array.

Solusinya bisa menggunakan forEach seperti itu.

var elList = document.querySelectorAll(".some .selector");
elList.forEach(function(el) {
    if (el.innerHTML.indexOf("needle") !== -1) {
        // Do what you like with el
        // The needle is case sensitive
    }
});

Ini bekerja bagi saya untuk melakukan pencarian / penggantian teks di dalam nodelist ketika pemilih normal tidak dapat memilih hanya satu node jadi saya harus memfilter setiap node satu per satu untuk memeriksa jarumnya.

Vigilante
sumber
2

Gunakan XPath dan document.evaluate (), dan pastikan untuk menggunakan text () dan bukan. untuk argumen berisi (), atau Anda akan memiliki seluruh HTML, atau elemen div terluar yang cocok.

var headings = document.evaluate("//h1[contains(text(), 'Hello')]", document, null, XPathResult.ANY_TYPE, null );

atau abaikan spasi di depan dan di belakangnya

var headings = document.evaluate("//h1[contains(normalize-space(text()), 'Hello')]", document, null, XPathResult.ANY_TYPE, null );

atau cocokkan semua jenis tag (div, h1, p, dll.)

var headings = document.evaluate("//*[contains(text(), 'Hello')]", document, null, XPathResult.ANY_TYPE, null );

Kemudian lakukan iterasi

let thisHeading;
while(thisHeading = headings.iterateNext()){
    // thisHeading contains matched node
}
Steven Spungin
sumber
Bisakah metode ini digunakan untuk menambahkan kelas ke elemen? misalnyathisheading.setAttribute('class', "esubject")
Matius
Setelah Anda memiliki elemennya, tentu. Namun, lebih baik menggunakan element.classList.add ("esubject") meskipun :)
Steven Spungin
1

Berikut pendekatan XPath tetapi dengan minimal jargon XPath.

Seleksi reguler berdasarkan nilai atribut elemen (untuk perbandingan):

// for matching <element class="foo bar baz">...</element> by 'bar'
var things = document.querySelectorAll('[class*="bar"]');
for (var i = 0; i < things.length; i++) {
    things[i].style.outline = '1px solid red';
}

Pemilihan XPath berdasarkan teks dalam elemen.

// for matching <element>foo bar baz</element> by 'bar'
var things = document.evaluate('//*[contains(text(),"bar")]',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
for (var i = 0; i < things.snapshotLength; i++) {
    things.snapshotItem(i).style.outline = '1px solid red';
}

Dan berikut ini dengan case-insensitivity karena teks lebih mudah menguap:

// for matching <element>foo bar baz</element> by 'bar' case-insensitively
var things = document.evaluate('//*[contains(translate(text(),"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz"),"bar")]',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
for (var i = 0; i < things.snapshotLength; i++) {
    things.snapshotItem(i).style.outline = '1px solid red';
}
Jan Kyu Peblik
sumber
0

Saya memiliki masalah yang sama.

Fungsi yang mengembalikan semua elemen yang menyertakan teks dari arg.

Ini bekerja untuk saya:

function getElementsByText(document, str, tag = '*') {
return [...document.querySelectorAll(tag)]
    .filter(
        el => (el.text && el.text.includes(str))
            || (el.children.length === 0 && el.outerText && el.outerText.includes(str)))

}

Paweł Zieliński
sumber
0

Sudah ada banyak solusi hebat di sini. Namun, untuk memberikan solusi yang lebih efisien dan satu lagi sesuai dengan gagasan perilaku querySelector dan sintaks, saya memilih solusi yang memperluas Object dengan beberapa fungsi prototipe. Kedua fungsi ini menggunakan ekspresi reguler untuk mencocokkan teks, namun, string dapat disediakan sebagai parameter pencarian longgar.

Cukup terapkan fungsi berikut:

// find all elements with inner text matching a given regular expression
// args: 
//      selector: string query selector to use for identifying elements on which we 
//                should check innerText
//      regex: A regular expression for matching innerText; if a string is provided,
//             a case-insensitive search is performed for any element containing the string.
Object.prototype.queryInnerTextAll = function(selector, regex) {
    if (typeof(regex) === 'string') regex = new RegExp(regex, 'i'); 
    const elements = [...this.querySelectorAll(selector)];
    const rtn = elements.filter((e)=>{
        return e.innerText.match(regex);
    });
    
    return rtn.length === 0 ? null : rtn
}

// find the first element with inner text matching a given regular expression
// args: 
//      selector: string query selector to use for identifying elements on which we 
//                should check innerText
//      regex: A regular expression for matching innerText; if a string is provided,
//             a case-insensitive search is performed for any element containing the string.
Object.prototype.queryInnerText = function(selector, text){
    return this.queryInnerTextAll(selector, text)[0];
}

Dengan penerapan fungsi ini, Anda sekarang dapat melakukan panggilan sebagai berikut:

  • document.queryInnerTextAll('div.link', 'go');
    Ini akan menemukan semua divs yang berisi link yang kelas dengan kata pergi di innerText (misalnya. Go Kiri atau GO bawah atau ke kanan atau Hal ini Go od )
  • document.queryInnerText('div.link', 'go');
    Ini akan bekerja persis seperti contoh di atas kecuali itu hanya akan mengembalikan elemen pertama yang cocok.
  • document.queryInnerTextAll('a', /^Next$/);
    Temukan semua tautan dengan teks persis Berikutnya (peka huruf besar-kecil). Ini akan mengecualikan link yang berisi kata Next bersama dengan teks lainnya.
  • document.queryInnerText('a', /next/i);
    Temukan tautan pertama yang berisi kata berikutnya , terlepas dari kasusnya (mis. Halaman Berikutnya atau Ke berikutnya )
  • e = document.querySelector('#page');
    e.queryInnerText('button', /Continue/);
    Ini melakukan pencarian dalam elemen wadah untuk tombol yang berisi teks, Lanjutkan (peka huruf besar-kecil). (mis. Lanjutkan atau Lanjutkan ke Berikutnya tetapi tidak melanjutkan )
b_laoshi
sumber