Reputation:
I'm trying to write a function that returns all elements with an immediate child text node -
function getAllElementsWithDirectTextNode() {
var matchingElements = [];
var allElements = document.body.getElementsByTagName('*');
for (var i = 0, n = allElements.length; i < n; i++) {
if (allElements[i].childNodes !== null) {
matchingElements.push(allElements[i]);
}
}
console.log(allElements);
}
However, this is not working. I think it has to do with the 5th line (the if statement). If the element has no direct text node children but does have a direct element child that has its own child text, I do not want to include it.
EDIT: I tried Praveen Kumar's solution with the following DOM, and the console prints all the elements in the body. However, I was expecting to only see div.div1, span.span2, div.div3, and button. Am I doing something wrong?
<div class="div1">
this is direct text
<span class="span1"></span>
</div>
<div class="div2">
<span class="span2">this is span text</span>
</div>
<article></article>
<div class="div3">
<span class="span3"></span>
this is direct text, child #2
</div>
Upvotes: 3
Views: 774
Reputation: 19535
The answers provided so far are fine, but a bit dated. There are some modern ECMAScript and DOM features you can use nowadays:
const getAllElementsWithDirectTextNode = () => Array.from(document.body.querySelectorAll("*"))
.filter(({ childNodes }) => Array.from(childNodes)
.some(({ nodeType, textContent }) => nodeType === Node.TEXT_NODE && textContent.trim()))
The .trim
check is there to test if removing white space from both ends of the text node still produces a non-empty string.
Remove it if you also want to include white space only results:
const getAllElementsWithDirectTextNode = () => Array.from(document.body.querySelectorAll("*"))
.filter(({ childNodes }) => Array.from(childNodes)
.some(({ nodeType }) => nodeType === Node.TEXT_NODE))
The Node.prototype.childNodes
getter will never return null
, so any comparison with null
is superfluous.
Upvotes: -1
Reputation: 1834
$(function () {
var textNodeIterator = document.createNodeIterator(
document.body,
NodeFilter.SHOW_TEXT,
function (node) {
if (is_ignorable(node)) return NodeFilter.FILTER_REJECT;
return NodeFilter.FILTER_ACCEPT;
});
var matchingElements = [];
var currentNode;
while (currentNode = textNodeIterator.nextNode()) {
//console.log(currentNode);
if (currentNode) {
//console.log(currentNode.nodeType + "-" + currentNode.textContent);
if (!isParentAlreadyMatched(currentNode.parentNode)) matchingElements.push(currentNode.parentNode);
}
}
console.log(matchingElements);
function is_all_ws(nod) {
// Use ECMA-262 Edition 3 String and RegExp features
return !(/[^\t\n\r ]/.test(nod.textContent));
}
function is_ignorable(nod) {
return (nod.nodeType == 8) || // A comment node
((nod.nodeType == 3) && is_all_ws(nod)); // a text node, all ws
}
function isParentAlreadyMatched(parentNode) {
for (var i = 0; i < matchingElements.length; i++) {
if (matchingElements[i] === parentNode) return true;
}
return false;
}
});
Upvotes: 1
Reputation: 1834
jsfiddle with a for loop solution
function getAllElementsWithDirectTextNode() {
var matchingElements = [];
var allElements = document.body.getElementsByTagName('*');
for (var i = 0; i < allElements.length; i++) {
for (var j=0; j < allElements[i].childNodes.length; j++) {
if (allElements[i].childNodes[j].nodeType === Node.TEXT_NODE) {
if (is_all_ws(allElements[i].childNodes[j])) continue;
matchingElements.push(allElements[i]);
break;
}
}
}
console.log(matchingElements);
}
function is_all_ws(nod) {
// Use ECMA-262 Edition 3 String and RegExp features
return !(/[^\t\n\r ]/.test(nod.textContent));
}
Upvotes: 1
Reputation: 782499
You need to test whether any of the children are text nodes. So you need to loop through all the children, testing their type.
function getAllElementsWithDirectTextNode() {
var matchingElements = [];
var allElements = document.body.getElementsByTagName('*');
for (var i = 0, n = allElements.length; i < n; i++) {
if (allElements[i].childNodes !== null) {
var children = allElements[i].childNodes;
for (var j = 0, m = children.length; j < m; j++) {
if (children[j].nodeType == Node.TEXT_NODE) {
matchingElements.push(allElements[i]);
break; // don't need to check the remaining children
}
}
}
}
console.log(allElements);
}
Upvotes: 1
Reputation: 167240
You need to check if they have a childNode
with nodeType
of Node.TEXT_NODE
:
function getAllElementsWithDirectTextNode() {
var matchingElements = [];
var allElements = document.body.getElementsByTagName('*');
for (var i = 0, n = allElements.length; i < n; i++) {
if (allElements[i].childNodes !== null) {
for (var j in allElements[i].childNodes)
if (allElements[i].childNodes[j].nodeType == Node.TEXT_NODE) {
matchingElements.push(allElements[i]);
break;
}
}
}
console.log(allElements);
}
Upvotes: 3