<div>div-1 <span>div-1-span-1 <div>div-1-span-1-div</div> <span>div-1-span-1-span</span> </span> </div>
I am trying to search this DOM. My search criteria is innerText, which is “div-1”. If that innerText is found, then I want to return the value as “div-1-span-1-div” and “div-1-span-1-span” which are again the innerText. How to achieve this using Javascript.
Advertisement
Answer
While I’m unsure of the logic behind the requirements I believe the following code seems to do what you ask:
// declaring a named function that takes two arguments: // selector: String, a CSS selector to determine the elements you're trying to search, // needle: String, a string of text that you're searching for to identify a given element: const findElementByText = (selector, needle) => { // here we convert the iterable NodeList returned from document.querySelectorAll() // into an Array, using the Array.prototype.from() method: return Array.from( // we pass the 'selector' argument to document.querySelectorAll() // to find all matching elements within the document: document.querySelectorAll(selector) // we then filter the resulting Array, using Array.prototype.filter() // which retains, or discards, Array-elements based on truthy/falsey // results of assessments within: ).filter( // using the anonymous Arrow function, we retrieve the childNodes of // each found element-node returned from document.querySelectorAll(), // 'el' is a reference to the current element-node of the Array of // element-nodes over which we're iterating: (el) => { // here we declare a variable, converting the HTMLCollection returned // by Node.childNodes into an Array of nodes in order to use Array- // methods such as Array.prototype.some(): let children = Array.from(el.childNodes); // we use Array.prototype.some() to test if some of the Array-elements // match the supplied tests; if so the method returns a Boolean true // otherwise, if no Array-element matches, it returns a Boolean false: return children.some( // here we use the anonymous Arrow function, and we check that some // of the childNodes (referenced as 'child' within the function body) // are of nodeType === 3 (a textNode) and that the childNode's nodeValue // once trimmed of leading/trailing whitespace is equal to the // supplied String: (child) => child.nodeType === 3 && child.nodeValue.trim() === needle ); // here we use Array.prototype.map() to construct a new Array based on // the Array-elements retained by Array.prototype.filter(): }).map( // again, using an anonymous Arrow function, passing a reference to // the current element-node into the function: // first we create an Array from the iterable HTMLCollection of the // current element-node's children: (el) => Array.from( el.children // we then use Array.prototype.map() to create a new Array // based on those childNodes: ).map( // here we create another Array from the children of the // previous child (since you seem to explicitly want the // child-elements of the <span> in your posted code: (child) => Array.from(child.children) // we then use Array.prototype.flat() to collapse the Array // to only one-dimension: ).flat() // and then again, we use Array.prototype.map() to map the // textContent of each child: .map( (child) => child.textContent.trim() // and finally we flatten the multidimensional Array: ).flat() ).flat(); }; console.log(findElementByText('div', 'div-1'));
*, ::before, ::after { box-sizing: border-box; font-size: 1rem; line-height: 1.5; margin: 0; padding: 0; } div, span { border: 1px solid var(--indicator); display: block; width: 90%; margin: 0.2em auto; } div { --indicator: lime; } span { --indicator: lightblue; }
<div>div-1 <span>div-1-span-1 <div>div-1-span-1-div</div> <span>div-1-span-1-span</span> </span> </div>
References: