How to change spaces to <br>s in a text node? - javascript

I am writing a Greasemonkey script in which in one place I'm converting all the spaces in the text into <br> so that the string appears more vertical in its cell in the table.
The HTML that I'm matching is:
<span>
<img class="icon itemicon" alt="Manual item" ...etc...>
Chapter 2 reading
<a title="Sort in descending order" href="index.php....."> etc </a>
</span>
Here is my code:
var child = spanelm.firstChild;
var textChild = child.nextSibling;
textChild.textContent = textChild.textContent.replace(/ /g, "<br>");
My Greasemonkey script is finding the right element and doing the change successfully, but on the web page I see the <br> tags literally:
Chapter<br>2<br>reading
(In another place in my code, I'm doing a similar thing, which seems to work:
spanelm.firstChild.innerHTML = spanelm.firstChild.text.replace(/ /g, "<br>");
but I cannot get that to work in this case, with this organization of the HTML.)
I feel like I need to tell Greasemonkey to eval() the HTML or something...

A text node doesn't have an innerHTML, so you can't insert new tags that way. (You should be very careful about this kind of string-replace of any node using innerHTML -- it's only a matter of time before the code trashes nested elements, inline JS, attached event handlers, etc.)
To insert tags in a text element, you need to break up that element into new text elements, interspersed with the tags you desire. In this case, replacing each run of spaces with a <br> would use code like this:
(See it in action at jsFiddle.)
var precedingNode = document.querySelector ("span > img.icon.itemicon");
var node = precedingNode.nextSibling;
var words = node.textContent.split (/ +/);
var parent = node.parentNode;
for (var J = 0, numWords = words.length; J < numWords; ++J) {
var newWord = document.createTextNode (words[J]);
var newBreak = document.createElement ("br");
parent.insertBefore (newWord, node);
parent.insertBefore (newBreak, node);
}
parent.removeChild (node);

Related

JavaScript createElement("h4") parses, creates text node, but doesn't create H4 element

I'm trying to have a for loop create H4 tags containing text, followed by some paragraph text, then repeat.
Here's what I've done:
var results = document.getElementById("results"); //refers to a <p> in html file
var stopper = 29;
for(i=0; i <= stopper; i++) {
var elementDate = document.createElement("h4");
elementDate.appendChild(document.createTextNode("Text gathered from an array"));
results.appendChild(elementDate);
results.innerText += "Paragraph text";
}
I also tried elementDate.innerText = "..." but I'm getting the same problem:
Instead of creating an H4 and appending the text, it just appends the text straight to <p id="results">. The H4 is never created and there are no errors in the log.
When you do
results.innerText += "Paragraph text";
What this does is:
Extracts the current innerText of the element, resulting in a plain string (no HTML tags)
Concatenates "Paragraph text" onto the end of that string
Sets that new string to be the new innerText of the element (no HTML tags)
So, the <h4> gets lost. (innerText should not be used in almost all situations anyway)
Use insertAdjacentText instead:
var results = document.getElementById("results"); //refers to a <p> in html file
var stopper = 29;
for(i=0; i <= stopper; i++) {
var elementDate = document.createElement("h4");
elementDate.appendChild(document.createTextNode("Some text which would be gathered from an array"));
results.appendChild(elementDate);
results.insertAdjacentText('beforeend', "Paragraph text");
}
<p id="results"></p>
(You might be tempted to do .innerHTML +=, but that would allow for arbitrary code execution, in addition to corrupting existing nodes in the container, which would be inelegant and would effectively deattach any event listeners attached to them)

How to highlight an editable word in dynamically generated text?

Intro
I am creating a content editor in which I want to add the functionality to choose a word which you would like to be highlighted while typing your content.
At this moment I achieved to search any word chosen in the #dynamicWord and then typed in #contentAreaContainer and give it a red border by adding em around the keyword and style the em trough CSS:
Part of the Code:
<div class="word">
Dynamic word to highlight: <input name="dynamic_word" id="dynamicWord" value="Enter word..">
</div>
<div id="contentAreaContainer" oninput="highlighter()">
<textarea id="contentArea"></textarea>
</div>
function highlighter()
{
var contentAreaContainer = document.getElementById('contentAreaContainer');
var dynamicWord = document.getElementById('dynamicWord').value;
wrapWord(contentAreaContainer, dynamicWord);
};
wrapWord() does:
function wrapWord(el, word)
{
var expr = new RegExp(word, "i");
var nodes = [].slice.call(el.childNodes, 0);
for (var i = 0; i < nodes.length; i++)
{
var node = nodes[i];
if (node.nodeType == 3) // textNode
{
var matches = node.nodeValue.match(expr);
if (matches)
{
var parts = node.nodeValue.split(expr);
for (var n = 0; n < parts.length; n++)
{
if (n)
{
var em = el.insertBefore(document.createElement("em"), node);
em.appendChild(document.createTextNode(matches[n - 1]));
}
if (parts[n])
{
el.insertBefore(document.createTextNode(parts[n]), node);
}
}
el.removeChild(node);
}
}
else
{
wrapWord(node, word);
}
}
}
em{border: 1px solid red;}
The problem:
Now at this moment every time on input in #contentAreaContainer the keyword chosen is highlighted a short period in the #contentAreaContainer, because highlighter() is triggered on input. But it should stay highlighted after finding it instead of only oninput.
I need oninput to search for the #dynamicWord value with wrapWord() while some one is typing;
Any time the #dynamicWord value was found it should permanently get an em
So how can I sort of 'save' the found keywords and permanently give them the element until the dynamic keyword gets edited?
Check the DEMO version
Solved:
Using setTimeout() instead of oninput I managed to make the highlight look constant. The change:
function highlighter()
{
var contentAreaContainer = document.getElementById('contentAreaContainer');
var mainKeyword = document.getElementById('main_keyword').value;
wrapWord(contentAreaContainer, mainKeyword);
repeater = setTimeout(highlighter, 0.1);
}
highlighter();
I removed oninput="highlighter()" from #contentAreaContainer.
You are trying to highlight words in a textarea. As far as I know a textarea does not support html elements inside. If you do it would simply display them as text.
Therefore you need to use an editable div. This is a normal div but if you add the attribute:
contentEditable="true"
the div acts like a textarea with the only difference it now process html elements. I also needed to change the onchange event into the onkeyup event. The editable div does not support onchange events so the highlight would not be triggered. The HTML for this div looks like:
<div contentEditable="true" id="contentArea">Test text with a word in it</div>
Here is the working code in a fiddle: http://jsfiddle.net/Q6bGJ/ When you enter a new character in the textarea your keyword gets highlighted.
However there is still a problem left. You surround the keyword with an em element. This results in surrounding it on every keystroke. Now you end up width many em's around the keyword. How to solve this, I leave up to you as a challenge.

replace specific tag name javascript

I want to know if we can change tag name in a tag rather than its content. i have this content
< wns id="93" onclick="wish(id)">...< /wns>
in wish function i want to change it to
< lmn id="93" onclick="wish(id)">...< /lmn>
i tried this way
document.getElementById("99").innerHTML =document.getElementById("99").replace(/wns/g,"lmn")
but it doesnot work.
plz note that i just want to alter that specific tag with specific id rather than every wns tag..
Thank you.
You can't change the tag name of an existing DOM element; instead, you have to create a replacement and then insert it where the element was.
The basics of this are to move the child nodes into the replacement and similarly to copy the attributes. So for instance:
var wns = document.getElementById("93");
var lmn = document.createElement("lmn");
var index;
// Copy the children
while (wns.firstChild) {
lmn.appendChild(wns.firstChild); // *Moves* the child
}
// Copy the attributes
for (index = wns.attributes.length - 1; index >= 0; --index) {
lmn.attributes.setNamedItem(wns.attributes[index].cloneNode());
}
// Replace it
wns.parentNode.replaceChild(lmn, wns);
Live Example: (I used div and p rather than wns and lmn, and styled them via a stylesheet with borders so you can see the change)
document.getElementById("theSpan").addEventListener("click", function() {
alert("Span clicked");
}, false);
document.getElementById("theButton").addEventListener("click", function() {
var wns = document.getElementById("target");
var lmn = document.createElement("p");
var index;
// Copy the children
while (wns.firstChild) {
lmn.appendChild(wns.firstChild); // *Moves* the child
}
// Copy the attributes
for (index = wns.attributes.length - 1; index >= 0; --index) {
lmn.attributes.setNamedItem(wns.attributes[index].cloneNode());
}
// Insert it
wns.parentNode.replaceChild(lmn, wns);
}, false);
div {
border: 1px solid green;
}
p {
border: 1px solid blue;
}
<div id="target" foo="bar" onclick="alert('hi there')">
Content before
<span id="theSpan">span in the middle</span>
Content after
</div>
<input type="button" id="theButton" value="Click Me">
See this gist for a reusable function.
Side note: I would avoid using id values that are all digits. Although they're valid in HTML (as of HTML5), they're invalid in CSS and thus you can't style those elements, or use libraries like jQuery that use CSS selectors to interact with them.
var element = document.getElementById("93");
element.outerHTML = element.outerHTML.replace(/wns/g,"lmn");
FIDDLE
There are several problems with your code:
HTML element IDs must start with an alphabetic character.
document.getElementById("99").replace(/wns/g,"lmn") is effectively running a replace command on an element. Replace is a string method so this causes an error.
You're trying to assign this result to document.getElementById("99").innerHTML, which is the HTML inside the element (the tags, attributes and all are part of the outerHTML).
You can't change an element's tagname dynamically, since it fundamentally changes it's nature. Imagine changing a textarea to a select… There are so many attributes that are exclusive to one, illegal in the other: the system cannot work!
What you can do though, is create a new element, and give it all the properties of the old element, then replace it:
<wns id="e93" onclick="wish(id)">
...
</wns>
Using the following script:
// Grab the original element
var original = document.getElementById('e93');
// Create a replacement tag of the desired type
var replacement = document.createElement('lmn');
// Grab all of the original's attributes, and pass them to the replacement
for(var i = 0, l = original.attributes.length; i < l; ++i){
var nodeName = original.attributes.item(i).nodeName;
var nodeValue = original.attributes.item(i).nodeValue;
replacement.setAttribute(nodeName, nodeValue);
}
// Persist contents
replacement.innerHTML = original.innerHTML;
// Switch!
original.parentNode.replaceChild(replacement, original);
Demo here: http://jsfiddle.net/barney/kDjuf/
You can replace the whole tag using jQuery
var element = $('#99');
element.replaceWith($(`<lmn id="${element.attr('id')}">${element.html()}</lmn>`));
[...document.querySelectorAll('.example')].forEach(div => {
div.outerHTML =
div.outerHTML
.replace(/<div/g, '<span')
.replace(/<\/div>/g, '</span>')
})
<div class="example">Hello,</div>
<div class="example">world!</div>
You can achieve this by using JavaScript or jQuery.
We can delete the DOM Element(tag in this case) and recreate using .html or .append menthods in jQuery.
$("#div-name").html("<mytag>Content here</mytag>");
OR
$("<mytag>Content here</mytag>").appendTo("#div-name");

How to use JavaScript or CSS to change a string on a page

How to use JavaScript or CSS to change a string on a page.
I will have around 50 elements which contain tags separated by commas ie
<a name="tagslist">Skrillex, dubstep, dance</a>
I want to using JavaScript take each of these words and style them differently so that they don't look like they are separated by commas but on different lines. The problem is there can be a variable amount of tag lists on the page.
Is there any way i can achieve this easily?
First of all, get out of last milennium and use this HTML:
<div id="tagslist">Skrillex, dubstep, dance</div>
Then you can do this:
var elm = document.getElementById('tagslist');
elm.innerHTML = elm.innerHTML.replace(/, /g,'<br />');
You can of course do something more complex, like this:
elm.innerHTML = "<div class='tag'>"+elm.innerHTML.replace(/, /g,"</div><div class='tag'>")+"</div>";
Alternatively, you can use real DOM methods:
var elm = document.getElementById('tagslist'), tags = elm.firstChild.nodeValue.split(", "),
l = tags.length, i, div;
elm.removeChild(elm.firstChild);
for( i=0; i<l; i++) {
div = elm.appendChild(document.createElement('div'));
div.appendChild(document.createTextNode(tags[i]));
// apply styles, className, etc. to div
}
$('a[name="tagslist"]').html(function(_, html){
var arr = [],
tags = html.split(/\s*,\s*/);
for (var i=0; i<tags.length; i++)
arr.push('<div class="whatever">' + tags[i] + '</div>');
return arr.join('');
});
Consider using an existing library such as http://ivaynberg.github.com/select2/#tags
You would have to have separate anchor tags for each one for them to be styled differently. For line breaks between them, you could put br tags between them.
What you want to do is not possible. A more ideal solution would be to have the html be rendered like so:
<span class="tagslist">
Skrillex
dubstep
dance
</span>
This way, you have complete control over the styling using CSS.

javascript HTML from document.body.innerHTML

I am trying to build a string of the contents of a webpage, without HTML syntax (probably replace it with a space, so words are not all conjoined) or punctuation.
so say you have the code:
<body>
<h1>Content:</h1>
<p>paragraph 1</p>
<p>paragraph 2</p>
<script> alert("blah blah blah"); </script>
This is some text<br />
....and some more
</body>
I want to return the string:
var content = "Content paragraph 1 paragraph 2 this is some text and this is some more";
any idea how to do this? Thanks.
You can use the innerText property (instead of innerHTML, which returns the HTML tags as well):
var content = document.getElementsByTagName("body")[0].innerText;
However, note that this will also include new lines, so if you are after exactly what you specified in your question, you would need to remove them.
There is the W3C DOM 3 Core textContent property supported by some browsers, or the MS/HTML5 innerText property supported by other browsers (some support both). Likely the content of the script element is unwanted, so a recursive traverse of the related part of the DOM tree seems best:
// Get the text within an element
// Doesn't do any normalising, returns a string
// of text as found.
function getTextRecursive(element) {
var text = [];
var self = arguments.callee;
var el, els = element.childNodes;
for (var i=0, iLen=els.length; i<iLen; i++) {
el = els[i];
// May need to add other node types here
// Exclude script element content
if (el.nodeType == 1 && el.tagName && el.tagName.toLowerCase() != 'script') {
text.push(self(el));
// If working with XML, add nodeType 4 to get text from CDATA nodes
} else if (el.nodeType == 3) {
// Deal with extra whitespace and returns in text here.
text.push(el.data);
}
}
return text.join('');
}
You'll need a striptags function in javascript for that and a regex to replace consecutive newlines with a single space.
You can try using the replace statement below
var str = "..your HTML..";
var content = str.replace(/</?[a-zA-Z0-9]+>|<[a-zA-Z0-9]+\s*/>|\r?\n/g," ");
For the HTML that you have provided above, this will give you the following string in content
Content: paragraph 1 paragraph 2 alert("blah blah blah"); This is some text ....and some more

Categories

Resources