How to change a specific element innerHTML with querySelectorAll('*') - javascript

I'm trying to build a code that will search through an entire page for a specific word and if it finds this word, it is supposed to change the dom of the found element.
With my current code I'm able to find the element but when I change the innerHTML, it is changing all the page content.
How can I only change the innerHTML of the elements with the word JavaScript?
var nodes = document.querySelectorAll('*');
for (var i = 0; i < nodes.length; i++) {
if(nodes[i].innerHTML.indexOf('JavaScript') !== -1) {
console.log(typeof nodes[i]);
nodes[i].innerHTML = 'text changed';
}
}
I know that the problem is because I'm targetting all the nodes, and for this reason, it is changing all the HTML. How can I prevent that and only change elements that matches JavaScript?

hi when you use nodes[i].innerHTML.indexOf('JavaScript') this your <body> and <html> has this word in innerHTML then change your document but i add new condition to if like this nodes[i].innerHTML.indexOf('<') == -1 that say only in child node find this string and in parent node don't check
var nodes = document.querySelectorAll('*');
for (var i = 0; i < nodes.length; i++) {
console.log(nodes[i].innerHTML.indexOf('<'))
if(nodes[i].innerHTML.indexOf('JavaScript') != -1 && nodes[i].innerHTML.indexOf('<') == -1 ) {
console.log(typeof nodes[i]);
nodes[i].innerHTML = 'text changed';
}
}
<div>
<p>hello <span>JavaScript</span></p>
<h1>hello World!</h1>
</div>

Related

Document evaluate doesn't work on 2 "line" string tag [duplicate]

Suppose I have a sentence in the webpage DOM that when I examine it, consists of 3 text nodes followed by perhaps some element like BOLD or ITALIC. I want to merge the text nodes into one text node, since having adjacent text nodes is meaningless - there is no reason to have them. Is there a way to merge them easily?
Thanks
It seems that Node.normalize() is doing exactly what you want.
You can refer to: Node.normalize()
Maybe this will help you:
var parentNode = document.getElementById('pelements');
var textNode = document.createElement('p');
while (parentNode.firstChild) {
textNode.textContent += parentNode.firstChild.textContent;
parentNode.removeChild(parentNode.firstChild);
}
parentNode.appendChild(textNode);
<div id="pelements">
<p>A</p>
<p>B</p>
<p>C</p>
</div>
It is possible, but you need to specify the parent element. It should be possible to traverse the whole DOM and every node, but if you can avoid that, it would be better.
nodes = document.body.childNodes;
nodesToDelete = [];
function combineTextNodes(node, prevText) {
if (node.nextSibling && node.nextSibling.nodeType == 3) {
nodesToDelete.push(node.nextSibling);
return combineTextNodes(node.nextSibling, prevText + node.nodeValue);
} else {
return prevText + node.nodeValue;
}
}
for (i = 0; i < nodes.length; i++) {
if (nodes[i].nodeType == 3) {
nodes[i].nodeValue = combineTextNodes(nodes[i], '');
}
}
for (i = 0; i < nodesToDelete.length; i++) {
console.log(nodesToDelete[i]);
nodesToDelete[i].remove();
}

Overriding constructor of Element interface in JavaScript

I would like to set id attributes for all the DOM elements by default. The IMPORTANT point is that it should happen during the construction of elements (when the browser is parsing HTML code and creating HTML elements and before appending them to the DOM tree). For example, if the HTML code of my website is like this:
<html>
<head>
<script>
//overriding constructor of Element
</script>
</head>
<body>
<p id='nH6Rf72Jk'> This is a paragraph </p>
<p> This is another paragraph </p>
</body>
</html>
I would like to have an id attribute (a random value) even for elements that do not explicitly an id has specified for them. I think it might be possible by overriding constructor of the Element interface (by the script that exists in the head). In other words, while browser is generating the elements and appending them to the DOM tree, check the id attribute; if it doesn't exist, generate a random value and set it as the id. Does anyone know if is possible to override the Element constructor? Or is there any other solution for this problem?
This should work
let all = document.getElementsByTagName("*");
for (let i=0; i < all.length; i++) {
if(all[i].getAttribute('id') === null){
all[i].setAttribute('id', makeId(6));
}
}
function makeId(length) {
let finalStr = '';
let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charsLength = chars.length;
for (i = 0; i < length; i++ ) {
finalStr += chars.charAt(Math.floor(Math.random() * charsLength));
}
return finalStr;
}
document.addEventListener('DOMContentLoaded', () => {
let elements = document.querySelectorAll('body *');
elements.forEach((elem => {
if (!elem.getAttribute('id')) {
elem.setAttribute('id', btoa(window.crypto.getRandomValues(new Uint32Array(1))[0]));
}
}));
});

Javascript querySelectorAll and :not Selector

<div class="classDiv">
<span id="mySpan"> TEXT 1 </span> TEXT 2
</div>
Using document.querySelectorAll(".classDiv")[0].textContent
Returns = TEXT 1 TEXT 2
How can i get only TEXT 2?
I tried with :not(span) without success.
Thanks
There’s nothing built-in that I’m aware of, but you can add a function to extract text nodes that are direct children of an element:
function getDirectText(node) {
var text = '';
for (var i = 0; i < node.childNodes.length; i++) {
var child = node.childNodes[i];
if (child.nodeType === 3) { // Node.TEXT_NODE
text += child.nodeValue;
}
}
return text;
}
and then:
var text = getText(document.getElementsByClassName('classDiv')[0]);
You might want to trim the result of whitespace, too.

JavaScript get textContent excluding children

First, I'm creating a library for JavaScript and I can not use jQuery. I'm trying to get the text content of an HTML element without the text contents of its children.
Both attributes innerText and textContent don't give me what needed, please help.
You can solve using DOM API as childNodes and nodeType.
var elChildNode = document.querySelector("#hello").childNodes;
var sChildTextSum = "";
elChildNode.forEach(function(value){
if(value.nodeType === Node.TEXT_NODE) {
console.log("Current textNode value is : ", value.nodeValue.trim());
sChildTextSum += value.nodeValue;
}
});
console.log("All text value of firstChild : ", sChildTextSum);
I created a sample code as above.
https://jsfiddle.net/nigayo/p7t9bdc3/
To get Author's Name from the following element, excluding <span>...:
<div class="details__instructor">
Author's Name<span ng-show="job_title">, Entrepreneur</span>
</div>
use childNodes[0]. For example:
document.querySelector('div.details__instructor').childNodes[0].textContent
Using only JavaScript (you specified you cannot use jQuery), and given that you have provided and know the id for the parent element:
document.getElementById('parent_element_id').childNodes[0].nodeValue;
You can also use .trim() to remove any trailing space characters left behind from the removal of any child element text:
document.getElementById('parent_element_id').childNodes[0].nodeValue.trim();
var mydiv = getElementByID("id");
function Get_text(element) {
var selected = element.cloneNode(true);
var text;
while (selected.firstChild) {
if (selected.firstChild.nodeType == 3) text = selected.firstChild.nodeValue;
selected.removeChild(selected.firstChild);
}
return text;
}
Get_text(mydiv);
I know many good solutions here exist, but none of them actually achieved what I needed (get the textContent of a single node, none of its children), so sharing this for future searchers.
var html = document.getElementsByTagName("*");
for (var i = 0; i < html.length; i++) {
var el = html[i];
for (var j = 0; j < el.children.length; j++) {
var child = el.children[j],
childTextContent = child.innerHTML;
// Remove all children tags, leaving only the actual text of the node.
childTextContent = childTextContent.replace(/\<.*\>.*\<\/.*\>/gmi, "");
// Also remove <img /> type tags.
childTextContent = childTextContent.replace(/\<.*\ \/\>/gmi, "");
console.log(childTextContent);
// Now you can do any type of text matching (regex) on the result.
}
});

how to dynamically address a word/string with javascript in an html-document and then tag it?

I have an HTML-document:
<html>
<body>
<p>
A funny joke:
<ul>
<li>do you know why six is afraid of seven?
<li>because seven ate nine.
</ul>
Oh, so funny!
</p>
</body>
</html>
Now I want to identify the first occurence of "seven" and tag it with
<span id="link1" class="link">
How can this be accomplished?
Do you have to parse the DOM-tree or is it possible to get the whole code within the body-section and then search for the word?
In both cases, after I found the word somewhere, how do you identify it and change it's DOM-parent to span (I guess that's what has to be done) and then add the mentioned attributes?
It's not so much a code I would expect, but what methods or concepts will do the job.
And I am not so much intersted in a framework-solution but in a pure javascript way.
You need to find a DOM node with type TEXT_NODE (3) and containig your expected word. When you need to split a that node into three ones.
First is a TEXT_NODE which contains a text before the word you search, second one is a SPAN node containing the word you search, and third one is another TEXT_NODE containing an original node's tail (all after searched word).
Here is a source code...
<html>
<head>
<style type="text/css">
.link {
color: red;
}
</style>
</head>
<body>
<p>
A funny joke:
<ul>
<li>do you know why six is afraid of seven?
<li>because seven ate nine.
</ul>
Oh, so funny!
</p>
<script type="text/javascript">
function search(where, what) {
var children = where.childNodes;
for(var i = 0, l = children.length; i < l; i++) {
var child = children[i], pos;
if(child.nodeType == 3 && (pos = child.nodeValue.indexOf(what)) != -1) { // a TEXT_NODE
var value = child.nodeValue;
var parent = child.parentNode;
var start = value.substring(0, pos);
var end = value.substring(pos + what.length);
var span = document.createElement('span');
child.nodeValue = start;
span.className = 'link';
span.id = 'link1';
span.innerHTML = what;
parent.appendChild(span);
parent.appendChild(document.createTextNode(end));
return true;
} else
if(search(child, what))
break;
}
return false;
}
search(document.getElementsByTagName('body')[0], 'seven');
</script>
</body>
</html>
This is a function I’ve written a few years ago that searches for specific text, and highlights them (puts the hits in a span with a specific class name).
It walks the DOM tree, examining the text content. Whenever it finds a text node containing the looked-for text, it will replace that text node by three new nodes:
one text node with the text preceding the match,
one (newly created) span element containing the matching text,
and one text node with the text following the match.
This is the function as I have it. It’s part of a larger script file, but it should run independently as well. (I’ve commented out a call to ensureElementVisible which made the element visible, since the script also had folding and expanding capabilities).
It does one (other) thing that you probably won’t need: it turns the search text into a regular expression matching any of the multiple words.
function findText(a_text, a_top) {
// Go through *all* elements below a_top (if a_top is undefined, then use the body)
// and check the textContent or innerText (only if it has textual content!)
var rexPhrase = new RegExp(a_text.replace(/([\\\/\*\?\+\.\[\]\{\}\(\)\|\^\$])/g, '\\$1').replace(/\W+/g, '\\W*')
, 'gi');
var terms = [];
var rexSearchTokens = /[\w]+/g;
var match;
while(match = rexSearchTokens.exec(a_text)) {
terms.push(match[0]);
}
var rexTerm = new RegExp('\\b(' + terms.join('|') + ')', 'gi');
var hits = [];
walkDOMTree(a_top || document.body,
function search(a_element) {
if (a_element.nodeName === '#text') {
if(rexPhrase.test(a_element.nodeValue)) {
// ensureElementVisible(a_element, true);
hits.push(a_element);
}
}
});
// highlight the search terms in the found elements
for(var i = 0; i < hits.length; i++) {
var hit = hits[i];
var parent = hit.parentNode;
if (parent.childNodes.length === 1) {
// Remove the element from the hit list
hits.splice(i, 1);
var text = hit.nodeValue;
parent.removeChild(hit);
var match, prevLastIndex = 0;
while(match = rexTerm.exec(text)) {
parent.appendChild(document.createTextNode(text.substr(prevLastIndex, match.index - prevLastIndex)));
var highlightedTerm = parent.appendChild(document.createElement('SPAN'));
highlightedTerm.className = 'search-hit';
highlightedTerm.appendChild(document.createTextNode(match[0]));
prevLastIndex = match.index + match[0].length;
// Insert the newly added element into the hit list
hits.splice(i, 0, highlightedTerm);
i++;
}
parent.appendChild(document.createTextNode(text.substr(prevLastIndex)));
// Account for the removal of the original hit node
i--;
}
}
return hits;
}
I found the following so question:
Find text string using jQuery?
This appears to be close to what you're trying to do. Now are you attempting to wrap just the text "seven" or are you attempting to wrap the entire content of the <li>?

Categories