Let's say I have the following:
var div = document.createElement('div');
div.innerHTML = "<";
var nodes = div.childNodes;
console.log(nodes)
then in all fields of nodes[0] (i.e. nodes[0].data, nodes[0].nodeValue, nodes[0].textContent, nodes[0].wholeText) I get <. Can I retrieve the "raw" value of a text node somehow? In this case <. Or is the only option to first retrieve it parsed, and then to escape the html somewhat like this:
function escapeHtml(html) {
var text = document.createTextNode(html);
var div = document.createElement('div');
div.appendChild(text);
return div.innerHTML;
}
Note: I consciously chose childNodes in order to get text-nodes as well as non-text-nodes.
Related
I have this XML string which I am displaying as a text in a document:
<p>The new strain of <s alias="coronavirus">COVID</s>seems to be having a greater spread rate.</p>
The following function returns the text form of the XML:
function stripHtml(html) {
// Create a new div element
var temporalDivElement = document.createElement("div");
// Set the HTML content with the providen
temporalDivElement.innerHTML = html;
// Retrieve the text property of the element (cross-browser support)
return temporalDivElement.textContent || temporalDivElement.innerText || "";
}
The problem is, this function returns the following string:
The new strain of COVIDseems to be having a greater spread rate.
which is nearly what I want, but there is no space between the word COVID and seems. Is it possible that I can add a space between contents of two tags if it doesn't exist?
One option is to iterate over text nodes and insert spaces at the beginning if they don't exist, something like:
const getTextNodes = (parent) => {
var walker = document.createTreeWalker(
parent,
NodeFilter.SHOW_TEXT,
null,
false
);
var node;
var textNodes = [];
while(node = walker.nextNode()) {
textNodes.push(node);
}
return textNodes;
}
function stripHtml(html) {
// Create a new div element
var temporalDivElement = document.createElement("div");
// Set the HTML content with the providen
temporalDivElement.innerHTML = html;
// Retrieve the text property of the element (cross-browser support)
for (const node of getTextNodes(temporalDivElement)) {
node.nodeValue = node.nodeValue.replace(/^(?!\s)/, ' ');
}
return temporalDivElement.textContent.replace(/ +/g, ' ').trim();
}
console.log(stripHtml(`<p>The new strain of <s alias="coronavirus">COVID</s>seems to be having a greater spread rate.</p>`));
I guess it will not let me because it is returning a string. The error I get is "Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'". How can I write this piece of code correctly?
getWord() {
let words = ["Movies", "Series", "DC Comics", "Batman"];
let word = words[Math.floor(Math.random() * words.length)];
let div = document.createElement("div");
div.appendChild(word);
}
Try div.innerText = word. Because you are trying to insert a String as a node.
If you want to stick with the div.appendChild() so that you could change the styling or element tag name of the text node in the future, you could create a text node and append it to the div instead, like so:
var text = document.createTextNode(word);
div.appendChild(text);
in your example you trying append string in the element, it's not correct, an argument for appendChild method should be element, for example:
const parent = document.createElement("div");
parent.appendChild(document.createElement("div"));
For your case, when you need to add content to the element, you should use textNode:
const title = document.createElement("H1");
const text = document.createTextNode("Movies");
title.appendChild(text);
Or:
const title = document.createElement("H1");
title.textContent = "Series";
In pure javascript (not using JQuery/dojo/etc), what is the best/easiest/quickest way to split a string, such as
var tempString = '<span id="35287845" class="smallIcon" title="time clock" style="color:blue;font-size:14px;" contenteditable="false">cookie</span>';
into
var id = 'id="35287845"';
var class = 'class="smallIcon"';
var title = 'title="time clock"';
var style = 'style="color:blue;font-size:14px;"';
var contenteditable = 'contenteditable="false"';
Things to note:
a "space" cannot be used as a proper delimiter, since it may appear in a value, such as title, above (time clock).
maintaining the double quotes around each variable, such as id="35287845" is important
the opening/closing span tags can be discarded, as well as the content, which in this case, is "cookie"
Here is one approach, which is to place the input string as innerhtml into a javascript created dom element and then leverage the attributes array
//Input html string
var tempString = '<span id="35287845" class="smallIcon" title="time clock" style="color:blue;font-size:14px;" contenteditable="false">cookie</span>';
//make element to contain html string
var tempDiv = document.createElement("div");
//place html string as innerhtml to temp element
tempDiv.innerHTML = tempString;
//leverage attributes array on element
var attributeArray = tempDiv.firstChild.attributes;
//log results
console.log(attributeArray);
Note that you may now do something like
var classString = attributeArray.class;
or
var titleString = attributeArray.title;
Edit
Here is a function that will do it:
function getAttributesFromString(htmlString)
{
var tempDiv = document.createElement("div");
tempDiv.innerHTML = htmlString;
return tempDiv.firstChild.attributes;
}
I think you are trying to get the properties in the span, check this response telling you how to do it.
Get all Attributes from a HTML element with Javascript/jQuery
also you could get the properties and make the string concatenating the the values with your strings.
(You can fin a explanation in pure javascript there)
I want to add an element to a textNode. For example: I have a function that search for a string within element's textNode. When I find it, I want to replace with a HTML element. Is there some standard for that?
Thank you.
You can't just replace the string, you'll have to replace the entire TextNode element, since TextNode elements can't contain child elements in the DOM.
So, when you find your text node, generate your replacement element, then replace the text node with a function similar to:
function ReplaceNode(textNode, eNode) {
var pNode = textNode.parentNode;
pNode.replaceChild(textNode, eNode);
}
For what it appears you want to do, you will have to break apart the current Text Node into two new Text Nodes and a new HTML element. Here's some sample code to point you hopefully in the right direction:
function DecorateText(str) {
var e = document.createElement("span");
e.style.color = "#ff0000";
e.appendChild(document.createTextNode(str));
return e;
}
function SearchAndReplaceElement(elem) {
for(var i = elem.childNodes.length; i--;) {
var childNode = elem.childNodes[i];
if(childNode.nodeType == 3) { // 3 => a Text Node
var strSrc = childNode.nodeValue; // for Text Nodes, the nodeValue property contains the text
var strSearch = "Special String";
var pos = strSrc.indexOf(strSearch);
if(pos >= 0) {
var fragment = document.createDocumentFragment();
if(pos > 0)
fragment.appendChild(document.createTextNode(strSrc.substr(0, pos)));
fragment.appendChild(DecorateText(strSearch));
if((pos + strSearch.length + 1) < strSrc.length)
fragment.appendChild(document.createTextNode(strSrc.substr(pos + strSearch.length + 1)));
elem.replaceChild(fragment, childNode);
}
}
}
}
Maybe jQuery would have made this easier, but it's good to understand why all of this stuff works the way it does.
I want to insert my own custom tags and scripts around the selected text. Something like this
var range = window.getSelection().getRangeAt(0);
var sel = window.getSelection();
range.setStart( sel.anchorNode, sel.anchorOffset );
range.setEnd(sel.focusNode,sel.focusOffset);
highlightSpan = document.createElement("abbr");
highlightSpan.setAttribute("style","background-color: yellow;");
highlightSpan.setAttribute("onmouseout","javascript:HideContentFade(\"deleteHighlight\");");
highlightSpan.setAttribute("onmouseover","javascript:ShowHighlighter(\"deleteHighlight\",\""+id_val+"\");");
highlightSpan.appendChild(range.extractContents());
range.insertNode(highlightSpan);
This works in normal scenarios but if I select some text in different paragraphs the extractContents API will validate the HTML returned and put additional tags to make it valid HTML. I want the exact HTML that was selected without the additional validating that javascript did.
Is there any way this can be done?
I have tried it the way mentioned in How can I highlight the text of the DOM Range object? but the thing is I want user specific highlights so if A has added some highlight B should not be able to see it. For this I have my backend code ready.
If you wrap with tags the selected text that belongs to different paragraphs, you create invalid HTML code.
This is an example of invalid HTML code that you would generate.
<p>notselected <span>selected</p><p>selected</span> notselected</p>
In order to accomplish your task, you need to wrap with tags each text in each paragraph of the selection resulting in a code like this.
<p>notselected <span>selected</span></p><p><span>selected</span> notselected</p>
To accomplish this you have to iterate over all nodes selected and wrap the selected text like this:
function wrapSelection() {
var range, start, end, nodes, children;
range = window.getSelection().getRangeAt(0);
start = range.startContainer;
end = range.endContainer;
children = function (parent) {
var child, nodes;
nodes = [];
child = parent.firstChild;
while (child) {
nodes.push(child);
nodes = nodes.concat(children(child));
child = child.nextSibling;
}
return nodes;
}
nodes = children(range.commonAncestorContainer);
nodes = nodes.filter(function (node) {
return node.nodeType === Node.TEXT_NODE;
});
nodes = nodes.slice(nodes.indexOf(start) + 1, nodes.indexOf(end));
nodes.forEach(function (node) {
wrap = window.document.createElement("span");
node.parentNode.insertBefore(wrap, node);
wrap.appendChild(node);
});
start = new Range();
start.setStart(range.startContainer, range.startOffset);
start.setEnd(range.startContainer, range.startContainer.length);
start.surroundContents(window.document.createElement("span"));
end = new Range();
end.setStart(range.endContainer, 0);
end.setEnd(range.endContainer, range.endOffset);
end.surroundContents(window.document.createElement("span"));
}