Use javascript to change text only in an element [duplicate] - javascript

This question already has answers here:
How can I change an element's text without changing its child elements?
(16 answers)
Closed 1 year ago.
Is there a simple way to change the text of an element only using vanilla javascript? In the code below, I thought that using .textContent, rather than .innerHTML would change the text and leave the image behind.
<head>
<script>
function change_stuff() {
var div = document.getElementById('to_change');
div.textContent = "OMG...it's an image!";
}
</script>
</head>
<body>
<div id="to_change">
This is a huge block of text that I want to replace while leaving the image in place
<img src="./the_image.jpg">
</div>
<button onclick="change_stuff();">
ThE dOER!!
</button>
</body>
I've also tried but had little to no success with many variations of this:
function change_stuff() {
var div = document.getElementById('to_change');
var text = div.textContent;
div.textContent = text.replace(text, "");
}
Any help would be appreciated

Get the first textNode by firstChild property and update the content.
function change_stuff() {
// get the first child node, in your code which is the text node
var t = document.getElementById('to_change').firstChild;
// update the text contents in the node
t.nodeValue = "";
// or t.textContent = "";
// or remove the node itself
// t.parentNode.removeChild(t)
}
<div id="to_change">
This is a huge block of text that I want to replace while leaving the image in place
<img src="./the_image.jpg">
</div>
<button onclick="change_stuff();">
ThE dOER!!
</button>

In the W3C DOM (Document Object Model), everything is a "node". Nodes come in different types (comment nodes, element nodes, attribute nodes and even text nodes). It may seem counter-intuitive that an element like div that doesn't have any nested elements that can contain text inside it actually does implicitly have a child element within it that contains the raw text and that element is a text node.
In order to access that (which will be separate from other elements within the div, you can navigate to the div and look for (in this case, it's firstChild because the text comes first and the image is second.
Also, when it comes to replacing the original text with something else...You were trying to call the .replace() string function on the div and not the text within the div. You can isolate just the text of the div by navigating to the text node within it and working just on that.
function change_stuff() {
// Get a reference to the div element's text node which is a child node
// of the div.
var divText = document.getElementById('to_change').firstChild;
// Get the current text within the element:
var text = divText.textContent;
// You can do whatever you want with the text (in this case replace)
// but you must assign the result back to the element
divText.textContent = text.replace(text, "");
}
<div id="to_change">
This is a huge block of text that I want to replace while leaving the image in place
<img src="./the_image.jpg">
</div>
<button onclick="change_stuff();">
ThE dOER!!
</button>

Or the pragmatic:
function change_stuff() {
var div = document.getElementById('to_change'),
img = div.getElementsByTagName('img')[0];
div.innerHTML = "OMG...it's an image!";
div.appendChild(img);
}
<div id="to_change">
This is a huge block of text that I want to replace while leaving the image in place
<img src="./the_image.jpg">
</div>
<button type="button" onclick="change_stuff();">
ThE dOER!!
</button>

You need to use innerText to set the text within the div (i.e.: div.innerText = replacement).
See Node.textContent - Differences from innerText.

Related

CSS Style Doesn't Work After createElement

I created a word counting function and found a discrepancy. It produced different results counting the text words in html depending on if the element the html is enclosed in is part of the document.body or not. For example:
html = "<div>Line1</div><div>Line2<br></div>";
document.body.insertAdjacentHTML("afterend", '<div id="node1"></div>');
node1 = document.getElementById("node1");
node1.style.whiteSpace = 'pre-wrap';
node1.innerHTML = html;
node2 = document.createElement('div');
node2.style.whiteSpace = 'pre-wrap';
node2.innerHTML = html;
The white-space: pre-wrap style is applied so that the code in the html variable is rendered, in terms of line-breaks, consistently across browsers. In the above:
node1.innerText // is "Line1\nLine2\n" which counts as two words.
node2.innerText // is "Line1Line2" which counts as only one word.
My word count function is:
function countWords(s) {
s = (s+' ').replace(/^\s+/g, ''); // remove leading whitespace only
s = s.replace(/\s/g, ' '); // change all whitespace to spaces
s = s.replace(/[ ]{2,}/gi,' ')+' '; // change 2 or more spaces to 1
return s.split(' ').filter(String).length;
}
If I then did something like this in the Web Console:
node1.after(node2);
node2.innerText // is changed to "Line1\nLine2\n" which counts as two words.
My questions are:
Why is the white-space: pre-wrap style not being applied to node 2.innerText before it is inserted into the document.body?
If node 2 has to be a part of document.body in order to get a white-space: pre-wrap style node 2.innerText value, how do I do that without having to make node 2 visible?
I'm curious. When I crate a node element with createElement, where does that node element reside? It doesn't appear to be viewable in a Web Console Inspector inside or outside of the <html> tag and I can't find it in the document object.
This tipped me off that the discrepancy was something to do with if the node element being in the document.body or not: javascript createElement(), style problem.
Indeed, when the element is attached to the DOM, Element.innerText takes the rendered value into account - you can say, the visible output. For non-attached elements, there is no rendering. The CSS properties exist but are not executed.
If you want consistent results between attached and non-attached elements, use Element.textContent.
For more information, see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText
In follow-up to my question above, I needed to count the words in html text strings like this: <div>Line1</div><div>Line2<br></div> where the word count matched what it would be if that html was rendered in the displayed DOM
To summarize what others have said, when you create an element using createElement it isn’t inserted into the DOM yet and can’t be found when inspecting the DOM. Before the element is inserted into the DOM, the CSS properties exist but are not executed, so there is no rendering. When the element is inserted into the DOM, the CSS properties are executed, and the element is rendered according to the CSS.
Here's the html-string-to-rendered-html-text function I ended up using. This function strips the html tags but retains the "white space" so that the words can then be counted (with consistency across browsers, including IE 11).
var html = "<div>Line1</div><div>Line2<br></div>";
// Display the html string
var htmlts = document.getElementById("htmlts");
htmlts.innerText = html;
// Display a DOM render of the html string
var node1 = document.getElementById("node1");
node1.style.whiteSpace = 'pre-wrap';
node1.innerHTML = html;
// Display the innerText of the above DOM render
var node1ts = document.getElementById("node1ts");
node1ts.innerText = node1.innerText;
// Display the results of the htmlToText function
var node2ts = document.getElementById("node2ts");
node2ts.innerText = htmlToText(html);
// Adapted from https://stackoverflow.com/a/39157530
function htmlToText(html) {
var temp = document.createElement('div');
temp.style.whiteSpace = 'pre-wrap';
temp.style.position = "fixed"; // Overlays the normal flow
temp.style.left = "0"; // Placed flush left
temp.style.top = "0"; // Placed at the top
temp.style.zIndex = "-999"; // Placed under other elements
// opacity = "0" works for the entire temp element, even in IE 11.
temp.style.opacity = "0"; // Everything transparent
temp.innerHTML = html; // Render the html string
document.body.parentNode.appendChild(temp); // Places just before </html>
var out = temp.innerText;
// temp.remove(); // Throws an error in IE 11
// Solution from https://stackoverflow.com/a/27710003
temp.parentNode.removeChild(temp); // Removes the temp element
return out;
}
<html lang="en-US">
<body>
HTML String: <code id="htmlts"></code><br><br>
Visible Render of HTML String (for comparison): <div id="node1"></div><br>
Visible Render Text String: <code id="node1ts"></code><br>
Function Returned Text String: <Code id="node2ts"></code><br>
</body>
</html>
If you prefer to have the temporary element insert inside the body element, change document.body.parentNode.appendChild to document.body.appendChild.
As Noam had suggested, you can also use temp.style.top = "-1000px";.
To answer my curiosity question: before the element is "inserted into the DOM" it appears to be in a Shadow DOM or Shadow Dom-like space.

Text always leaves it's container when ever there is space around the text

I was trying to insert an element to an existing element. But each time I insert it, the text inside the element node to be appended will leave the element and stays outside the initial container, I found out that each time I called .trim() on it, it won't leave the container.
E.g
let txt = " space around the text ";
let node = `<span styly="prop:val;">${txt}</span>`;
let div = document.getElementById("myDiv");
div.innerHTML = node;
The result of the above code will be:
<div id="myDiv">
space around the text
<span style="prop:val"></span> //nothing inside the span
</div>
If I remove the spaces in the txt i.e calling on .trim() on the txt it will work as expected.
E.g:
let txt = " space around the text ";
let node = `<span style="prop:Val;">${txt.trim()}</span>`;
let div = document.getElementById("myDiv");
div.innerHTML = node;
The result of the above code will be: (it works fine now)
<div id="myDiv">
<span style="prop:val">space around the text</span>
</div>
I haven't experienced such problem, I don't know if it is JavaScript error or Dom parsing.
Thanks in advance.
I have figured out the problem with my code.
I was matching all new line and replacing them with <li> tag, so each time it matches it, it breaks out of the parent container.
Example;
I have an HTML code like below.
<div style=color=red>
<span style=color=green>element
<span style=color=blue> inner </span>
</span>
Some texts....
</span>
So matching every new line and replacing it with a <li> the element <li> will cause HTML parser to close the first <span> causing the text "Some text...." not being able to inherit the style color "green" from it's parent element.
The solution to my problem is closing every element before replacing new line with <li> as simple as that.

How to get text around an element?

If I have
<div id='wrapper'>
<fieldset id='fldset'>
<legend>Title</legend>
Body text.
</fieldset>
</div>
How could I retrieve the "Body text" without retrieving the text inside of legend tag?
For instance, if I wanted to see if the text inside the fieldset contains the word "body" and change that word, while ignoring whatever might be in the legend? innerHTML retrieves all text from all children nodes.
Use of jQuery wouldn't be a problem.
$("#fldset").contents().eq(2).text();
Without a library --
var fldset = document.getElementById("fldset"),
txt = fldset.lastChild.textContent || fldset.lastChild.innerText;
alert(txt);
This will get all the text nodes of fldset leaving out any other element and it's content:
var fldsetContent = $('#fldset').contents();
var text = '';
$(fldsetContent).each( function(index, item) {
if( item.nodeType == 3 ) text += $.trim($(item).text());
});
alert( text );
Live example
$('#fldset').clone().find('legend').remove().end().text()
But you should also search around the SO
Using .text() to retrieve only text not nested in child tags
Clip content with jQuery
I can't think of a way other than
$("#result").html($("#wrapper").text().replace($("legend").text(),""));
but there should be a more elegant way. You can also create a new element as a copy of this one, remove all the children and get text. Hmm... That would be:
var newElement = $("#fldset").clone();
newElement.children().remove();
$("#result").html(newElement.text());
So doesn't matter how many and which type of children node has, this would work. Here: http://www.jsfiddle.net/wFV4c/
To turn all plain text nodes inside the field set red:
jQuery.each($('#fldset').contents(),function(index,value){
if(value.textContent == value.nodeValue){
$(this).wrap('<span style="color:red;" />')
}
});

Javascript: Dynamic Generation of a Div

I have several vertically stacks divs and I want to have a div appear when I click a button within each of these divs. The div that I want to appear will be the exact same for each appearance with the exception of an id associating it with the outer div. How do I do this in Javascript?
I assume I should use the createElement() within Javascript, but how do I append it to the end of a specific element. Also, when creating an element, I have to hardcode the html in the Javascript file. Is there anyway to leave the element within the html design file. I want to seperate design from code as much as possible.
Clone Node
var clone = myDiv.cloneNode();
Example (live demo):
HTML
<div>
<input type="button" onclick="functionClone(this);" value="Dolly"/>
<input type="button" onclick="functionClone(this);" value="Dolly_1"/>
</div>
Javascript:
functionClone = function(subject){
var clonenode = subject.cloneNode(true);
subject.value = subject.value + '\'s been cloned!';
subject.disabled = true;
insertElementAfter(subject, clonenode);
}
insertElementAfter = function(subject, newElement){
subject.parentNode.insertBefore(newElement,subject.nextSibling);
}
To append an element below your div use this:
subject.parentNode.appendChild(clonenode)

Remove all text in between a div without losing text outside of it - jQuery JS

I have some text that is stored in a variable like this:
<div class="foo">text inside div</div>
outside text here.
I want to remove the entire div and the text in it and save the outside text.
Create an element based off the HTML. Then,
$('.foo', context).remove();
For example:
var text = "<div class=\"foo\">text inside div</div>\noutside text here.";
var xml = $('<root>' + text + '</root>');
$('.foo', xml).remove();
text = xml.html();
You can use after, to insert the inner text after the div, and then remove it:
var $foo = $(".foo");
$foo.after($foo.text()).remove();
Or you could use the replaceWith function, to replace the div element with its inner text content:
$foo.replaceWith($foo.text());

Categories

Resources