Javascript .querySelector ค้นหา <div> โดย innerTEXT


116

ฉันจะค้นหา DIV พร้อมข้อความบางอย่างได้อย่างไร ตัวอย่างเช่น:

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

พยายามใช้สิ่งนี้:

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

แต่แน่นอนมันจะไม่ทำงาน ฉันจะทำมันได้อย่างไร?


แม้ว่าคุณจะทำได้ แต่ก็จะไม่เร็วไปกว่าการรับ div ทั้งหมดและกรองผ่านคุณสมบัติ innerText ทำไมคุณไม่ทำด้วยตนเอง
Redu

คำตอบ:


108

คำถาม OP เป็นเรื่องเกี่ยวกับธรรมดาJavaScriptและไม่jQuery แม้ว่าจะมีคำตอบมากมายและฉันชอบคำตอบของ @Pawan Nogariya โปรดตรวจสอบทางเลือกนี้

คุณสามารถใช้XPATHใน JavaScript ข้อมูลเพิ่มเติมเกี่ยวกับบทความ MDN ที่นี่

document.evaluate()วิธีการประเมินผลการสอบถาม XPATH / การแสดงออก ดังนั้นคุณสามารถส่งนิพจน์ XPATH ที่นั่นสำรวจเข้าไปในเอกสาร HTML และค้นหาองค์ประกอบที่ต้องการ

ใน XPATH คุณสามารถเลือกองค์ประกอบโดยโหนดข้อความดังต่อไปนี้ whch รับสิ่งdivที่มีโหนดข้อความต่อไปนี้

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

หากต้องการรับองค์ประกอบที่มีข้อความให้ใช้ดังต่อไปนี้:

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

contains()วิธีการในการใช้เวลา XPATH โหนดเป็นพารามิเตอร์แรกและข้อความเพื่อค้นหาเป็นพารามิเตอร์ที่สอง

ตรวจสอบกลุ่มนี้ที่นี่นี่คือตัวอย่างการใช้ XPATH ใน JavaScript

นี่คือข้อมูลโค้ด:

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

อย่างที่คุณเห็นฉันสามารถจับองค์ประกอบ HTML และปรับเปลี่ยนได้ตามต้องการ


ขอบคุณ! ใช้งานได้ดี! แต่จะ "console.log" "thisHeading.textContent" ได้อย่างไรถ้าฉันต้องการเพียงคำเดียวจากข้อความนี้? ตัวอย่างเช่น: '// div [มี (., \' / คุณเข้าสู่ระบบ (. *) คูณเซสชันนี้ / \ ')]' แล้วแจ้งเตือน (thisHeading.textContent $ 1)
passwd

โอเคฉันทำแบบนี้:alert(thisHeading.textContent.replace(/.*You have login (.*) times.*/,'$1')) ;
passwd

@passwd คุณทำอย่างนั้นไม่ได้ ไม่รองรับ Regex ใน XPATH 1.0 (ซึ่ง.evaluate()ใช้โปรดมีคนแก้ไขฉันถ้าฉันผิด) ประการแรกคุณไม่สามารถค้นหาสิ่งที่ตรงกับนิพจน์ทั่วไปได้ ประการที่สอง.textContentคุณสมบัติจะส่งคืนโหนดข้อความขององค์ประกอบ หากคุณต้องการดึงค่าออกจากข้อความนี้คุณควรจัดการกับมันอย่างชัดเจนโดยอาจเป็นการสร้างฟังก์ชันบางประเภทที่ตรงกับนิพจน์ทั่วไปและส่งคืนค่าที่ตรงกันในกลุ่มสำหรับคำถามนั้นให้สร้างคำถามใหม่ในเธรดที่แยกต่างหาก
gdyrrahitis

Internet Explorer: ไม่รองรับ แต่รองรับใน Edge ฉันไม่แน่ใจว่ามันหมายถึงอะไรเวอร์ชันฉลาด
Rolf

ควรจัดการกับข้อผิดพลาดอย่างไรในกรณีที่องค์ประกอบที่ฉันกำลังมองหาหายไป
nenito

76

คุณสามารถใช้วิธีง่ายๆนี้:

Array.from(document.querySelectorAll('div'))
  .find(el => el.textContent === 'SomeText, text continues.');
  1. Array.fromจะแปลง NodeList ไปยังอาร์เรย์ (มีหลายวิธีที่จะทำเช่นนี้ผู้ประกอบการแพร่กระจายหรือชิ้น)

  2. ผลลัพธ์ตอนนี้คืออาร์เรย์อนุญาตให้ใช้Array.findเมธอดจากนั้นคุณสามารถใส่เพรดิเคตใดก็ได้ คุณยังสามารถตรวจสอบ textContent ด้วยนิพจน์ทั่วไปหรืออะไรก็ได้ที่คุณต้องการ

โปรดทราบว่าArray.fromและArray.findเป็นคุณสมบัติของ ES2015 Te เข้ากันได้กับเบราว์เซอร์รุ่นเก่าเช่น IE10 โดยไม่ต้องใช้ Transpiler:

Array.prototype.slice.call(document.querySelectorAll('div'))
  .filter(function (el) {
    return el.textContent === 'SomeText, text continues.'
  })[0];

4
หากคุณต้องการที่จะหาองค์ประกอบหลายแทนที่ด้วยfind filter
RubbelDieKatz

40

เนื่องจากคุณถามในจาวาสคริปต์เพื่อให้คุณสามารถมีสิ่งนี้ได้

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

แล้วเรียกแบบนี้

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

1
ดูเหมือนว่าจะได้ผล แต่ในทางกลับกันฉันได้รับสิ่งนี้เท่านั้น:[object HTMLDivElement],[object HTMLDivElement]
passwd

ใช่คุณจะได้รับ divs ที่มีข้อความที่ตรงกันจากนั้นคุณสามารถเรียกวิธีการข้อความด้านในแบบนี้ได้foundDivs[0].innerTextง่ายๆ
Pawan Nogariya

21

โซลูชันนี้ทำสิ่งต่อไปนี้:

  • ใช้ตัวดำเนินการสเปรด ES6 เพื่อแปลง NodeList ของ all divs เป็นอาร์เรย์

  • ให้การส่งออกถ้าdiv มีสตริงการสืบค้นที่ไม่เพียง แต่ถ้ามันตรงเท่ากับสตริงการสืบค้น (ซึ่งเกิดขึ้นสำหรับบางส่วนของคำตอบอื่น ๆ ) เช่นควรให้ผลลัพธ์ไม่เพียง แต่สำหรับ 'SomeText' แต่ยังรวมถึง 'SomeText, text ต่อไป'

  • แสดงdivเนื้อหาทั้งหมดไม่ใช่เฉพาะสตริงแบบสอบถาม เช่นสำหรับ "SomeText ข้อความจะดำเนินต่อไป" ควรส่งออกทั้งสตริงไม่ใช่แค่ "SomeText"

  • ช่วยให้หลาย ๆที่จะมีสตริงไม่เพียงหนึ่งเดียวdivdiv

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


4
ฉันรักสิ่งนี้. สะอาดกระชับและเข้าใจได้ทั้งหมดในเวลาเดียวกัน
ba_ul

3
ไม่มีประสิทธิภาพอย่างน่ากลัวแน่ ๆ ? คิดว่าใหญ่แค่ไหนinnerHTMLสำหรับคนที่เก่งที่สุด<div>ของคุณ คุณควรกรองdivs ที่มีลูกก่อน ยังสงสัยว่าdocument.getElementsByTagName('div')อาจเร็วกว่า แต่ฉันจะเปรียบเทียบเพื่อให้แน่ใจ
Timmmm

นี่เป็นสิ่งที่ดีสำหรับฉันฉันสามารถตั้งตัวเลือกที่ดีได้ในตอนเริ่มต้นเพราะฉันรู้อยู่แล้วว่ามันสามารถอยู่ในโต๊ะได้เท่านั้นขอบคุณ
gsalgadotoledo

11

คุณควรดูว่าคุณมีองค์ประกอบหลักของ div ที่คุณกำลังค้นหาหรือไม่ หากเป็นเช่นนั้นให้รับองค์ประกอบหลักและดำเนินการelement.querySelectorAll("div"). เมื่อคุณได้รับแล้วให้nodeListใช้ตัวกรองกับinnerTextคุณสมบัตินั้น สมมติว่าองค์ประกอบหลักของ div ที่ว่าเรามีการสอบถามมีของid containerโดยปกติคุณสามารถเข้าถึงคอนเทนเนอร์ได้โดยตรงจาก id แต่ลองทำตามวิธีที่เหมาะสม

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

นั่นแหล่ะ


สิ่งนี้ใช้ได้ผลสำหรับฉัน แต่ใช้ innerHTML แทน innerText
Chase Sandmann

5

หากคุณไม่ต้องการใช้ jquery หรืออะไรทำนองนั้นคุณสามารถลองสิ่งนี้:

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

เมื่อคุณมีโหนดในอาร์เรย์ที่มีข้อความแล้วคุณสามารถทำบางสิ่งกับพวกมันได้ เช่นเดียวกับการแจ้งเตือนแต่ละรายการหรือพิมพ์ลงในคอนโซล ข้อแม้อย่างหนึ่งคือสิ่งนี้อาจไม่จำเป็นต้องคว้า div ต่อ se ซึ่งจะจับพาเรนต์ของโหนดข้อความที่มีข้อความที่คุณกำลังมองหา


3

เนื่องจากไม่มีการจำกัดความยาวของข้อความในแอตทริบิวต์ข้อมูลให้ใช้แอตทริบิวต์ข้อมูล! จากนั้นคุณสามารถใช้ตัวเลือก css ปกติเพื่อเลือกองค์ประกอบของคุณตามที่ 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>

ตามหลักการแล้วคุณจะทำส่วนการตั้งค่าแอตทริบิวต์ข้อมูลในการโหลดเอกสารและ จำกัด ตัวเลือก querySelectorAll ให้แคบลงเล็กน้อยเพื่อประสิทธิภาพ


2

Google มีสิ่งนี้เป็นผลลัพธ์อันดับต้น ๆ สำหรับผู้ที่ต้องการค้นหาโหนดที่มีข้อความบางอย่าง โดยวิธีการอัปเดตตอนนี้ nodelist สามารถทำซ้ำได้ในเบราว์เซอร์สมัยใหม่โดยไม่ต้องแปลงเป็นอาร์เรย์

โซลูชันสามารถใช้ forEach เช่นนั้นได้

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

สิ่งนี้ได้ผลสำหรับฉันในการค้นหา / แทนที่ข้อความภายใน nodelist เมื่อตัวเลือกปกติไม่สามารถเลือกเพียงโหนดเดียวได้ดังนั้นฉันจึงต้องกรองแต่ละโหนดทีละโหนดเพื่อตรวจสอบเข็ม


2

ใช้ XPath และ document.evaluate () และตรวจสอบให้แน่ใจว่าใช้ text () ไม่ใช่ สำหรับอาร์กิวเมนต์มี () มิฉะนั้นคุณจะมี HTML ทั้งหมดหรือองค์ประกอบ div ด้านนอกสุดที่ตรงกัน

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

หรือไม่สนใจช่องว่างที่นำหน้าและต่อท้าย

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

หรือจับคู่แท็กทุกประเภท (div, h1, p ฯลฯ )

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

จากนั้นทำซ้ำ

let thisHeading;
while(thisHeading = headings.iterateNext()){
    // thisHeading contains matched node
}

วิธีนี้สามารถใช้เพื่อเพิ่มคลาสให้กับองค์ประกอบได้หรือไม่? เช่นthisheading.setAttribute('class', "esubject")
Matthew

เมื่อคุณมีองค์ประกอบแน่ใจ อย่างไรก็ตามควรใช้ element.classList.add ("esubject") ดีกว่า :)
Steven Spungin

1

นี่คือแนวทาง XPath แต่มีศัพท์แสง XPath ขั้นต่ำ

การเลือกปกติตามค่าแอตทริบิวต์องค์ประกอบ (สำหรับการเปรียบเทียบ):

// 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';
}

การเลือก XPath ตามข้อความภายในองค์ประกอบ

// 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';
}

และนี่คือกรณีที่ไม่ตอบสนองเนื่องจากข้อความมีความผันผวนมากขึ้น:

// 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';
}

0

ฉันมีปัญหาที่คล้ายกัน

ฟังก์ชันที่ส่งคืนองค์ประกอบทั้งหมดซึ่งรวมถึงข้อความจาก arg

สิ่งนี้ใช้ได้กับฉัน:

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

}


0

มีวิธีแก้ปัญหาที่ยอดเยี่ยมมากมายที่นี่แล้ว อย่างไรก็ตามเพื่อให้เป็นโซลูชันที่มีความคล่องตัวมากขึ้นและอีกหนึ่งวิธีที่สอดคล้องกับแนวคิดของพฤติกรรมและไวยากรณ์ของ querySelector ฉันเลือกใช้โซลูชันที่ขยายObjectด้วยฟังก์ชันต้นแบบสองสามฟังก์ชัน ฟังก์ชันทั้งสองนี้ใช้นิพจน์ทั่วไปสำหรับข้อความที่ตรงกันอย่างไรก็ตามสามารถระบุสตริงเป็นพารามิเตอร์การค้นหาแบบหลวม ๆ ได้

เพียงใช้ฟังก์ชันต่อไปนี้:

// 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];
}

เมื่อใช้ฟังก์ชันเหล่านี้คุณสามารถโทรออกได้ดังนี้:

  • document.queryInnerTextAll('div.link', 'go');
    สิ่งนี้จะพบdivทั้งหมดที่มีคลาสลิงก์ที่มีคำว่าgoใน innerText (เช่นGo LeftหรือGO downหรือgo rightหรือIt's Go od )
  • document.queryInnerText('div.link', 'go');
    สิ่งนี้จะทำงานได้เหมือนกับตัวอย่างด้านบนยกเว้นว่าจะส่งคืนเฉพาะองค์ประกอบที่ตรงกันแรกเท่านั้น
  • document.queryInnerTextAll('a', /^Next$/);
    ค้นหาลิงก์ทั้งหมดที่มีข้อความถัดไป (พิจารณาตัวพิมพ์เล็กและใหญ่) ซึ่งจะไม่รวมลิงก์ที่มีคำว่าNextพร้อมกับข้อความอื่น ๆ
  • document.queryInnerText('a', /next/i);
    ค้นหาลิงก์แรกที่มีคำถัดไปโดยไม่คำนึงถึงกรณี (เช่นหน้าถัดไปหรือไปที่ถัดไป )
  • e = document.querySelector('#page');
    e.queryInnerText('button', /Continue/);
    ดำเนินการค้นหาภายในองค์ประกอบคอนเทนเนอร์สำหรับปุ่มที่มีข้อความดำเนินการต่อ (พิจารณาตัวพิมพ์เล็กและใหญ่) (เช่นดำเนินการต่อหรือดำเนินการต่อไปยังถัดไปแต่ไม่ดำเนินการต่อ )
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.