How to get all the HTML nodes having text in an optimal way without having to loop through every node?
In other words, grab all HTML nodes having visible text.
For example, if I have a dom as below
<div>
<span>Hello This is a Text Span</span>
<div>
<p> This is a text Paragraph</p>
<button> This is Button Label</button>
</div>
<div> This is also a visible text</div>
</div>
I should select
span having text Hello This is a Text Span
p having text This is a text Paragraph
button having text This is Button Label
div having text This is also a visible text
The outermost div in the above example doesn't have text of its own so should not be part of the result.
Edit: What problem am I trying to solve?
The framework I use escapes HTML characters in labels of fields, buttons, headings etc.
For example: < is converted to & lt;'
So I am trying to write a client side code which triggers after the page is completely rendered which will unescape all the HTML texts to a readable format.
Any help will be appreciated.
Thanks in advance
The DOM property holds a numeric code indicating the node's type; text nodes use the code 3, So you can find those text nodes by filtering them having nodeType 3.
Wrap your all nodes in a div by giving a class.
Select your content by it's class like this: $(".getTextNodes").contents();.
Filter contents having nodeType 3.
selectedElement = $(".getTextNodes").contents();
textNodes = selectedElement.filter(function() {
return this.nodeType === 3;
});
console.log(textNodes);
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<div class="getTextNodes">
<span>Hello This is a Text Span</span>
<div>
<p> This is a text Paragraph</p>
<button> This is Button Label</button>
</div>
<div> This is also a visible text</div>
</div>
Check this link out to read more.
I'm using just only vanilla js
My algorithm is just select all element that has no element inside it
It means select all element that has directly Text Node
let el = document.querySelectorAll('div,span,p,button');
var arr = [];
el.forEach(function(m){
if (m.querySelectorAll('div,span,p,button').length == 0){
arr.push(m)
console.log(m)
}
})
// console.log(arr)
<div>
<span>Hello This is a Text Span</span>
<div>
<p>This is a text Paragraph</p>
<button> This is Button Label</button>
</div>
<div>This is also a visible text</div>
</div>
Jsfidle link click here
There's no css selector to get your needs and looping is only the solution.
Related
I am trying to use javascript to remove HTML elements <p><br></p>
const blog = document.getElementById('blog');
const postNode = document.createElement('div');
postNode.innerHTML = postNode.innerHTML.replace('<p><br></p>', '');
blog.appendChild(postNode);
However, the replace doesn't seem to get rid of the strings within the innerHTML. I think it is because the output is currently
<h2>This is a heading two</h2><p>This is a paragraph</p><p><br></p><p>This is a paragraph with a <strong>bold</strong> an <em>italic</em> and an <u>underscored</u> text</p><p><br></p>
The problem <p><br></p>HTML is connected to other elements and thus not a string or substring in it's own right.
Is there some regex magic that can help me out?
Iterate through <p> tags, and filter them by whether the tag has exactly one child, and that child is a <br>. Then, .remove() each of the <p>s which pass that test:
[...postNode.querySelectorAll('p')]
.filter(p => p.children.length === 1 && p.children[0].tagName === 'BR')
.forEach(p => p.remove());
console.log(postNode.innerHTML);
<div id="postNode">
<h2>This is a heading two</h2>
<p>This is a paragraph</p>
<p><br></p>
<p>This is a paragraph with a <strong>bold</strong> an <em>italic</em> and an <u>underscored</u> text</p>
<p><br></p>
</div>
I think instead you can use a class to hide the elements you want to hide with
display: none;
I am looking for a certain word, for instance "unique" on the current webpage to remove the content of the directly surrounding tag, but not more.
Example
<div>
<div>test unique is
</div>
</div>
<p>Hello
</p>
becomes
<div>
<div>
</div>
</div>
<p>Hello
</p>
Iow, innerText or innerHTML of the containing tag is set to ''.
So far, I did
<script>
var badDivs = $("div:contains('unique')");
badDivs.remove ();
</script>
but I want to
include all tags, not just div
only want the upmost tag to be set to '', not the whole page be blank because somewhere nested there is the word unique.
It should work with tags that have or don't have attributes as in the example
It appears as though you don't want to remove the tag, only its contents Since the selector *:contains('unique') selects every element, including the parents, you'll want to grab the last element selected.. See find, last, and empty.
$(function() {
$(document).find(":contains('unique')").not("script").last().empty();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<div>test unique is
</div>
</div>
<p>Hello
</p>
In the case of this Stack Snippet, the script in inserted into the body element in the iframe, so I've specifically excluded those elements.
Filter elements that contain the text but don't have any other children.
$(':contains("unique")').filter(function(){
return !$(this).children().length;
}).empty()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<div>test unique is
</div>
</div>
<p>Hello
</p>
This question already has answers here:
Using .text() to retrieve only text not nested in child tags
(30 answers)
Closed 5 years ago.
Could some one give me some guidance on what's the best way to do this.
I'm trying to get all the text which is after ".main"
I know this might be simple, but it's been picking at my brain all day just before Christmas. So i thought instead of stressing myself out, I would look for some guidance.
The example code only brings back the Text in P tag but i'd like to bring back Text not in it's own element and the p tag
console.log($("#container").find(".main").next().text());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<div class="main"> WOOP IN MAIN </div>
Text not in it's own element
<p> Text in P tag </p>
</div>
The simplest way to achieve this is to clone() the container, remove the .main element from it, then get the text(), like this:
var text = $("#container").clone().find('.main').remove().end().text();
console.log(text.trim());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<div class="main"> WOOP IN MAIN </div>
Text not in it's own element
<p> Text in P tag </p>
</div>
You could alternatively recursively traverse through the DOM nodes that follow the .main element, but this is much more complicated and gives the same result.
It's because Text not in it's own element is considered a text node, therefore next() will target the <p/> tag, being that it's an HTMLElement. If you were to go native you'd use a combination of nextSibling, which is agnostic of the two node types and nextElementSibling, which as it's method name implies, grabs the next sibling element:
const main = document.querySelector('.main');
const txt = [
main.nextSibling.textContent.trim(),
main.nextElementSibling.textContent.trim()
];
console.log(txt)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<div class="main"> WOOP IN MAIN </div>
Text not in it's own element
<p> Text in P tag </p>
</div>
I have a div with some text and a child div. I want to update the outer div text and keep the child div.
<div class="outer">
some text here
<div class="arrow-down"></div>
</div>
if I try outer.innerText = "foo" the arrow-down element is deleted. How can I get around this?
Thanks
Create a child element such as a span element and place the text you want change inside that.
Then you can update that via JavaScript like so:
var el = document.getElementById('inner');
el.innerText = 'some different text';
<div class="outer">
<span id="inner">some text here</span>
<div class="arrow-down"></div>
</div>
I like the usage of append in D3, and I'm looking for prepend.
Does this exist in D3?
You can use
selection.insert(newElement[, anotherExistingElement])
For example:
selection.insert("div",":first-child")
The above code will insert a div before the first child of selected element. Check documentation to learn more.
Another possible way of inserting elements before any node (including plain texts):
var parentEl = d3.select("div").node();
parentEl.insertBefore(document.createElement("div"), parentEl.childNodes[0]);
<script src="https://d3js.org/d3.v3.min.js"></script>
<div>
This is a plain text
<a></a>
</div>
Selection.lower()
selection.lower() will place an element as the first child of its parent.
Together with d3's append, selection.append().lower() can replicate jQuery's prepend
Since D3 v4+, D3 has both selection.raise() and selection.lower() methods. These are used most frequently to move elements in an SVG so that certain elements appear overtop of others, where ordering of SVG elements in the DOM determines draw order. But, they can be used for any element in the DOM.
Here's a quick demonstration using divs and paragraphs:
var div = d3.select("div");
div
.append("p")
.text("Inserted")
.lower();
console.log(div.html());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="div">
Text
<p> Child Paragraph </p>
</div>
The snippet takes a div with the following contents:
Text
<p> Child Paragraph </p>
And uses d3 to append a new paragraph and then lower it so that the structure is as follows:
<p>Inserted</p>
Text
<p> Child Paragraph </p>
And for comparison with with jQuery's prepend:
var div = $("div");
div
.prepend("<p>Inserted</p>");
console.log(div.html());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="div">
Text
<p> Child Paragraph </p>
</div>
More Info
The selection.lower() is implemented as such (see docs for more info):
selection.each(function() {
this.parentNode.insertBefore(this, this.parentNode.firstChild);
});