I'm trying to figure out what is the differences between this two:
// first one
var h1 = document.createElement('h1');
var t = document.createTextNode('hey');
h1.appendChild(t);
document.body.appendChild(h1);
// second one
document.body.appendChild(document.createElement('h1').appendChild(document.createTextNode('hey')));
The first (Document.createElement()) works perfectly, but the second (Document.createTextNode()) does not.
The return value of appendChild is the appended child.
So if we add variables to:
document.body.appendChild(document.createElement('h1').appendChild(document.createTextNode('hey')));
it gets broken down into:
var text = document.createTextNode('hey');
var h1 = document.createElement('h1');
h1.appendChild(text);
document.body.appendChild(text);
Appending the text to the body removes the text from the h1.
The h1 is discarded because it is never appended anywhere.
I find a way to do it: (just add .parentNode at the end)
document.body.appendChild(document.createElement('h1').appendChild(document.createTextNode('hey')).parentNode);
Related
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.
I want to create a div element on page load event but my script is not working as expected.
function createfn(){
//debugger;
var element = document.createElement("div");
var para = document.createTextNode('The man who mistook his wife for a hat');
element.appendChild(para);
document.getElementByTagName(body).appendChild(element);
}
window.onload=createfn();
What is it wrong with this code?
a few issues:
first the tag name body needs to be wrapped in quotes. in your code you are passing an undeclared variable called body.
Secondly, its getElementsByTagName() asthis function returns multiple elements in an array.
Lastly, you need to target the first body element:
function createfn(){
//debugger;
var element = document.createElement("div");
var para = document.createTextNode('The man who mistook his wife for a hat');
element.appendChild(para);
document.getElementsByTagName('body')[0].appendChild(element);
}
window.onload=createfn();
jsfiddle
Hi I am trying to add a html with a SPACE after in a contenteditable div;
The problem is that, with above code it only return the content of the first DIV and ignore everything else.
var tdiv = document.createElement('div');
tdiv.innerHTML = '<div>testing html</div> ';
var replacment = tdiv.firstChild; //
el.insertNode(replacment); // it is just the purpose, "el" is the HTML element
With this the nbsp will be removed.
If you want all children of tdiv to be added to el then try
var el = document.getElementById('x')
while (tdiv.firstChild) {
el.appendChild(tdiv.firstChild);
}
Demo: Fiddle
You can create an element with an nbsp in it like this:
var div = document.createElement("div");
div.innerHTML = " ";
If you really just want an element with a space of text, you can just create a text node:
var div = document.createElement("div");
div.appendChild(document.createTextNode(" "));
Looking at your question again, it is actually quite unclear what you're really asking and it appears that there are many different things your question might mean. So, this is just one possibility for what you might be asking.
If you want to add it as an element, then you need to put it in some sort of container since a non-breakingspace isn't an element by itself. You can wrap it in a <span> element like this and then insert the <span>.
var span = document.createElement("span");
span.innerHTML = " ";
el.appendChild(span);
Or, maybe it works just fine to insert a text node with a space in it:
el.appendChild(document.createTextNode(" "));
After Arun P Johny Idea I resolved like this:
var replacement = '<div>testing</div> ',startAfter,i,tdiv = document.createElement('div');
tdiv.innerHTML = replacment;
replacment=document.createDocumentFragment();
while(i=tdiv.firstChild) replacment.appendChild(i);
startAfter = replacment.lastChild;
// This is my extra code to the contenteditable div insert and positioning the caret - this.range is my selection range.
this.range.insertNode(replacment);
this.range.setStartAfter(startAfter);
Thank you all, even for the down votes :P
Here is an example. Check the console for the result. The first two divs (not appended; above the <script> in the console) have the proper spacing and indention. However, the second two divs do not show the same formatting or white space as the original even though they are completely the same, but appended.
For example the input
var newElem = document.createElement('div');
document.body.appendChild(newElem);
var another = document.createElement('div');
newElem.appendChild(another);
console.log(document.body.innerHTML);
Gives the output
<div><div></div></div>
When I want it to look like
<div>
<div></div>
</div>
Is there any way to generate the proper white space between appended elements and retain that spacing when obtaining it using innerHTML (or a possible similar means)? I need to be able to visually display the hierarchy and structure of the page I'm working on.
I have tried appending it within an element that is in the actual HTML but it has the same behavior
I'd be okay with doing it using text nodes and line breaks as lincolnk suggested, but it needs to affect dynamic results, meaning I cannot use the same .createTextNode(' </br>') because different elements are in different levels of the hierarchy
No jQuery please
I think you're asking to be able to append elements to the DOM, such that the string returned from document.body.innerHTML will be formatted with indentation etc. as if you'd typed it into a text editor, right?
If so, something like this might work:
function indentedAppend(parent,child) {
var indent = "",
elem = parent;
while (elem && elem !== document.body) {
indent += " ";
elem = elem.parentNode;
}
if (parent.hasChildNodes() && parent.lastChild.nodeType === 3 && /^\s*[\r\n]\s*$/.test(parent.lastChild.textContent)) {
parent.insertBefore(document.createTextNode("\n" + indent), parent.lastChild);
parent.insertBefore(child, parent.lastChild);
} else {
parent.appendChild(document.createTextNode("\n" + indent));
parent.appendChild(child);
parent.appendChild(document.createTextNode("\n" + indent.slice(0,-2)));
}
}
demo: http://jsbin.com/ilAsAki/28/edit
I've not put too much thought into it, so you might need to play with it, but it's a starting point at least.
Also, i've assumed an indentation of 2 spaces as that's what you seemed to be using.
Oh, and you'll obviously need to be careful when using this with a <pre> tag or anywhere the CSS is set to maintain the whitespace of the HTML.
You can use document.createTextNode() to add a string directly.
var ft = document.createElement('div');
document.body.appendChild(ft);
document.body.appendChild(document.createTextNode(' '));
var another = document.createElement('div');
document.body.appendChild(another);
console.log(document.body.innerHTML);
In my app there is an html file showed in a webview. I have a note functionality where when user selects text, it is highlighted and an image is added as suffix. This note is then saved as an html file.
So for this functionality, I have written a java script function.
function highlightsText()
{
var range = window.getSelection().getRangeAt(0);
var selectionContents = range.extractContents();
var newDate = new Date;
var randomnumber= newDate.getTime();
var div;
var imageTag = document.createElement("img");
imageTag.id=randomnumber;
imageTag.setAttribute("src","notes.png");
var linkTxt = document.createElement("a");
linkTxt.id=randomnumber;
linkTxt.setAttribute("href","highlight:"+randomnumber);
linkTxt.appendChild(selectionContents)
div = document.createElement("span");
div.style.backgroundColor = "yellow";
div.id=randomnumber;
linkTxt.appendChild(imageTag);
div.appendChild(linkTxt);
range.insertNode(div);
return document.body.innerHTML+"<noteseparator>"+randomnumber+"<noteseparator>"+range.toString();
}
Here I am making a span and this span holds my highlighted text with image.
Now problem is,
When I am selecting a paragraph, it only adds an image and does not highlight the text.
If I use div or p tag in place of span then it gives an entire line for a single word which looks rather odd.
Edit: div tags will get a linebreak before and after (usually, most browsers do this, considering it is a "division"/block level element), you're better off using a span.
And secondly you should append the selection contents to the span
ispain.appendChild(selectionContents) !! and do not forget the semicolon ;)
on a side note, you do know that:
1- you can't have html element ids starting with digits.
2- having more than one elements with the same id is gonna get unpredictable when you're selecting em.