Javascript .querySelector tìm thấy <div> bởi innerTEXT


108

Làm cách nào để tìm DIV với một số văn bản nhất định? Ví dụ:

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

Đang cố gắng sử dụng một cái gì đó như thế này:

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

Nhưng tất nhiên nó sẽ không hoạt động. Tôi làm nó như thế nào?


Ngay cả khi bạn có thể làm điều đó, nó sẽ không nhanh hơn là lấy tất cả các div và lọc chúng qua thuộc tính innerText. Vậy tại sao bạn không làm điều đó một cách thủ công.
Redu

Câu trả lời:


99

Câu hỏi của OP là về JavaScript thuần túy chứ không phải jQuery . Mặc dù có rất nhiều câu trả lời và tôi thích câu trả lời của @Pawan Nogariya , vui lòng kiểm tra câu trả lời thay thế này.

Bạn có thể sử dụng XPATH trong JavaScript. Thông tin thêm về bài viết MDN tại đây .

Các document.evaluate()phương pháp đánh giá một truy vấn XPath / biểu. Vì vậy, bạn có thể chuyển các biểu thức XPATH vào đó, chuyển vào tài liệu HTML và định vị phần tử mong muốn.

Trong XPATH, bạn có thể chọn một phần tử, bằng nút văn bản như sau, whch lấy phần tử divcó nút văn bản sau.

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

Để lấy một phần tử có chứa một số văn bản, hãy sử dụng như sau:

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

Các contains()phương pháp trong XPATH mất một nút như tham số đầu tiên và các văn bản để tìm kiếm như tham số thứ hai.

Kiểm tra đoạn này ở đây , đây là một ví dụ sử dụng XPATH trong JavaScript

Đây là một đoạn mã:

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

Như bạn có thể thấy, tôi có thể lấy phần tử HTML và sửa đổi nó theo ý muốn.


Cảm ơn bạn! Hoạt động tuyệt vời! Nhưng làm thế nào để "console.log" thành "thisHeading.textContent" nếu tôi chỉ cần lấy một từ từ văn bản này? Ví dụ: '// div [contains (., \' / Bạn đăng nhập (. *) Lần này vào phiên này / \ ')]' rồi cảnh báo (thisHeading.textContent. $ 1)
passwd

Được rồi, tôi làm theo cách này:alert(thisHeading.textContent.replace(/.*You have login (.*) times.*/,'$1')) ;
passwd

@passwd, bạn không thể làm điều đó. Regex không được hỗ trợ trong XPATH 1.0 ( .evaluate()sử dụng. Vui lòng ai đó sửa cho tôi nếu tôi sai), vì vậy trước tiên, bạn không thể tìm kiếm thứ gì đó phù hợp với biểu thức chính quy. Thứ hai, thuộc .textContenttính trả về nút văn bản của phần tử. Nếu bạn muốn lấy một giá trị từ văn bản này, bạn nên xử lý nó một cách rõ ràng, có thể bằng cách tạo một số loại hàm phù hợp với regex và trả về giá trị phù hợp trong nhóm. Vì vậy, hãy đặt một câu hỏi mới trên một chuỗi riêng.
gdyrrahitis

Internet Explorer: Không hỗ trợ. Nhưng được hỗ trợ trong Edge. Tôi không chắc điều đó có nghĩa là gì, theo phiên bản.
Rolf

Làm thế nào nên được xử lý một lỗi trong trường hợp phần tử tôi đang tìm kiếm bị thiếu?
nenito

70

Bạn có thể sử dụng giải pháp khá đơn giản này:

Array.from(document.querySelectorAll('div'))
  .find(el => el.textContent === 'SomeText, text continues.');
  1. Các Array.fromsẽ chuyển đổi các NodeList để một mảng (có nhiều phương pháp để thực hiện điều này như các nhà điều hành lây lan hoặc lát)

  2. Kết quả bây giờ là một mảng cho phép sử dụng Array.findphương thức, sau đó bạn có thể đặt bất kỳ vị từ nào. Bạn cũng có thể kiểm tra textContent bằng regex hoặc bất cứ thứ gì bạn thích.

Lưu ý rằng Array.fromArray.findlà các tính năng của ES2015. Tương thích với các trình duyệt cũ hơn như IE10 mà không có trình chuyển tiếp:

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

1
Nếu bạn muốn tìm nhiều phần tử, hãy thay thế findbằng filter.
RubbelDieKatz

38

Vì bạn đã hỏi nó trong javascript nên bạn có thể có một cái gì đó như thế này

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

Và sau đó gọi nó như thế này

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
Có vẻ như điều này hoạt động, nhưng đổi lại tôi chỉ nhận được điều này:[object HTMLDivElement],[object HTMLDivElement]
passwd

Có bạn sẽ được nhận được divs với văn bản phù hợp trong nó và sau đó bạn có thể gọi đó phương pháp văn bản bên trong một cái gì đó như thế này foundDivs[0].innerText, mà đơn giản
Pawan Nogariya

20

Giải pháp này thực hiện những điều sau:

  • Sử dụng toán tử trải phổ ES6 để chuyển đổi NodeList của tất cả các divs thành một mảng.

  • Cung cấp đầu ra nếu div có chứa chuỗi truy vấn, không chỉ nếu nó chính xác bằng chuỗi truy vấn (điều này xảy ra đối với một số câu trả lời khác). Ví dụ: Nó sẽ cung cấp đầu ra không chỉ cho 'SomeText' mà còn cho 'SomeText, văn bản tiếp tục'.

  • Xuất ra toàn bộ divnội dung, không chỉ chuỗi truy vấn. ví dụ: Đối với 'SomeText, văn bản tiếp tục' nó sẽ xuất ra toàn bộ chuỗi đó, không chỉ 'SomeText'.

  • Cho phép nhiều divs chứa chuỗi, không chỉ một chuỗi 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>


3
Tôi thích điều này. Sạch sẽ, ngắn gọn và dễ hiểu - tất cả cùng một lúc.
ba_ul

2
Chắc chắn là không hiệu quả kinh khủng? Hãy nghĩ xem lớn như thế nào innerHTMLđối với những người hàng đầu của bạn <div>. Bạn nên lọc ra divnhững cái có chứa trẻ em trước. Cũng nghi ngờ document.getElementsByTagName('div')có thể nhanh hơn nhưng tôi sẽ chuẩn để chắc chắn.
Timmmm

Điều này thật tuyệt đối với tôi, tôi có thể đặt một bộ chọn tốt ngay từ đầu vì tôi đã biết rằng nó chỉ có thể nằm trong bảng, thật tuyệt, cảm ơn
gsalgadotoledo

10

Tốt nhất bạn nên xem liệu bạn có phần tử mẹ của div mà bạn đang truy vấn hay không. Nếu vậy, hãy lấy phần tử cha và thực hiện một element.querySelectorAll("div"). Khi bạn nhận được, hãy nodeListáp dụng một bộ lọc trên nó trên thuộc innerTexttính. Giả sử rằng một yếu tố phụ huynh của div rằng chúng ta đang truy vấn có idcủa container. Bạn thường có thể truy cập vùng chứa trực tiếp từ id nhưng hãy làm theo cách thích hợp.

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

À chính nó đấy.


Điều này hiệu quả với tôi nhưng với innerHTML thay vì innerText
Chase Sandmann 28/09/18

5

Nếu bạn không muốn sử dụng jquery hoặc thứ gì đó tương tự thì bạn có thể thử cách này:

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

Khi bạn có các nút trong một mảng chứa văn bản, bạn có thể làm gì đó với chúng. Như cảnh báo từng cái hoặc in ra bảng điều khiển. Một lưu ý là điều này có thể không nhất thiết phải lấy div, điều này sẽ lấy cha mẹ của textnode có văn bản bạn đang tìm kiếm.


3

Vì không có giới hạn về độ dài của văn bản trong thuộc tính dữ liệu, hãy sử dụng thuộc tính dữ liệu! Và sau đó, bạn có thể sử dụng các bộ chọn css thông thường để chọn (các) phần tử của bạn như OP muốn.

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>

Lý tưởng nhất là bạn thực hiện phần cài đặt thuộc tính dữ liệu khi tải tài liệu và thu hẹp bộ chọn querySelectorAll một chút cho hiệu suất.


2

Google có đây là kết quả hàng đầu cho những người cần tìm một nút có văn bản nhất định. Bằng cách cập nhật, một danh sách nút hiện có thể lặp lại trong các trình duyệt hiện đại mà không cần phải chuyển đổi nó thành một mảng.

Giải pháp có thể sử dụng forEach như vậy.

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

Điều này giúp tôi thực hiện tìm / thay thế văn bản bên trong danh sách nút khi một bộ chọn thông thường không thể chọn chỉ một nút vì vậy tôi phải lọc từng nút một để kiểm tra kim.


2

Sử dụng XPath và document.evaluate () và đảm bảo sử dụng text () chứ không phải. đối với đối số chứa (), nếu không, bạn sẽ có toàn bộ HTML hoặc phần tử div ngoài cùng được khớp.

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

hoặc bỏ qua khoảng trắng đầu và cuối

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

hoặc khớp với tất cả các loại thẻ (div, h1, p, v.v.)

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

Sau đó lặp lại

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

Phương thức này có thể được sử dụng để thêm một lớp vào một phần tử không? ví dụthisheading.setAttribute('class', "esubject")
Matthew

Một khi bạn có phần tử, chắc chắn. Tuy nhiên, nó là tốt hơn để sử dụng element.classList.add ( "esubject") mặc dù :)
Steven Spungin

1

Đây là cách tiếp cận XPath nhưng với tối thiểu thuật ngữ XPath.

Lựa chọn thông thường dựa trên các giá trị thuộc tính phần tử (để so sánh):

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

Lựa chọn XPath dựa trên văn bản bên trong phần tử.

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

Và đây là phân biệt chữ hoa chữ thường vì văn bản dễ bay hơi hơn:

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

Tôi đã có vấn đề tương tự.

Hàm trả về tất cả phần tử bao gồm văn bản từ arg.

Điều này phù hợp với tôi:

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

Đã có rất nhiều giải pháp tuyệt vời ở đây. Tuy nhiên, để cung cấp một giải pháp hợp lý hơn và một giải pháp khác phù hợp với ý tưởng về hành vi và cú pháp của querySelector, tôi đã chọn một giải pháp mở rộng Đối tượng với một vài hàm nguyên mẫu. Cả hai hàm này đều sử dụng biểu thức chính quy để đối sánh văn bản, tuy nhiên, một chuỗi có thể được cung cấp dưới dạng tham số tìm kiếm lỏng lẻo.

Chỉ cần thực hiện các chức năng sau:

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

Với các chức năng này được triển khai, bây giờ bạn có thể thực hiện các cuộc gọi như sau:

  • document.queryInnerTextAll('div.link', 'go');
    Điều này sẽ tìm thấy tất cả các div có chứa lớp liên kết có từ go trong innerText (ví dụ: Đi sang trái hoặc ĐI xuống hoặc đi sang phải hoặc Đó là đi od )
  • document.queryInnerText('div.link', 'go');
    Điều này sẽ hoạt động chính xác như ví dụ ở trên ngoại trừ nó sẽ chỉ trả về phần tử phù hợp đầu tiên.
  • document.queryInnerTextAll('a', /^Next$/);
    Tìm tất cả các liên kết có văn bản chính xác Tiếp theo (phân biệt chữ hoa chữ thường). Điều này sẽ loại trừ các liên kết có chứa từ Tiếp theo cùng với văn bản khác.
  • document.queryInnerText('a', /next/i);
    Tìm liên kết đầu tiên có chứa từ tiếp theo , không phân biệt chữ hoa chữ thường (ví dụ: Trang Tiếp theo hoặc Chuyển đến tiếp theo )
  • e = document.querySelector('#page');
    e.queryInnerText('button', /Continue/);
    Thao tác này thực hiện tìm kiếm trong phần tử vùng chứa cho nút có chứa văn bản, Tiếp tục (phân biệt chữ hoa chữ thường). (ví dụ: Tiếp tục hoặc Tiếp tục đến Tiếp theo nhưng không tiếp tục )
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.