Convert text node into HTML element using JQuery - javascript

Consider the following HTML:
<div>
aaaa
<span>bbbb</span>
cccc
<span>dddd</span>
eeee
</div>
I use JQuery to match the [aaaa, cccc, eeee] text nodes by following the answers in this question:
$('div').contents().filter(function()
{
return this.nodeType === 3;
});
Now, I want to replace every text node with an HTML element - say a <div> containing the text node. This is my desired result:
<div>
<div>aaaa</div>
<span>bbbb</span>
<div>cccc</div>
<span>dddd</span>
<div>eeee</div>
</div>
I've tried to use various closures passed to .each. E.g.:
$('div').contents().filter(function()
{
return this.nodeType === 3;
}).each(function()
{
this.html("<div>" + this.text() + "</div>");
});
But it seems that the text nodes do not provide any .html method. How can I replace a text node with an arbitrary HTML element using JQuery?

this refers to a plain DOM node element that doesn't implement neither an html() nor a text() method. Using $(this), you can make the element into a jQuery collection in order to be able to access the jQuery methods. Then you can use replaceWith() to replace the plain text nodes with the <div>s.
$('div').contents().filter(function()
{
return this.nodeType === 3;
}).each(function()
{
$(this).replaceWith("<div>" + $(this).text() + "</div>");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
aaaa
<span>bbbb</span>
cccc
<span>dddd</span>
eeee
</div>

You can also use wrap from jquery to wrap the content with div
.wrap()
Description: Wrap an HTML structure around each element in the set of matched elements.
REF: http://api.jquery.com/wrap/
$('div').contents().filter(function()
{
return this.nodeType === 3;
}).each(function()
{
$(this).wrap('<div>');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
aaaa
<span>bbbb</span>
cccc
<span>dddd</span>
eeee
</div>

Related

How to get class name of element has specific text using javascript/jquery?

I need a JavaScript or jQuery way of extracting the Class name of DIV element by the text it contains.
Let's illustrate. If I had let's say following code:
<div class="_className">UniqueText</div>
I need to to know how to programmatically do something like this:
getClassNameWhereText("UniqueText");
In this case output should be:
_className
Is there a way to do this?
JQuery :contains selector select element has specific text but it isn't exact. For example
$("div:contains(UniqueText)")
Select both of bottom divs
<div class="_className">UniqueText</div>
<div class="_className2">UniqueText2</div>
You can use .filter() to filter selected element by text.
var className = $("*").filter(function(){
return $(this).text() == "UniqueText";
}).attr("class");
var className = $("*").filter(function(){
return $(this).text() == "UniqueText";
}).attr("class");
console.log(className);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="_className">UniqueText</div>
<div class="_className2">UniqueText2</div>
By getting all the div with each function you can search through all the divs and place a condition in which you the value of the div is equal to the particular text that you want to find. Then get the class name by using .attr('class').
$( "div" ).each(function(){
if($(this).text() == "UniqueText"){
var output = $(this).attr('class');
$(".output").html(output);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="_classname">UniqueText</div>
<div class="output"></div>
It might be a bit long for a code but it gets the work done nicely. :)
You can use :contains(word)
var className = $( "div:contains('John')" ).attr("class");
console.log(className)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="foo">John Resig</div>
<div class="bar">George Martin</div>
<div class="foo">Malcom John Sinclair</div>
<div class="baz">J. Ohn</div>
You can keep an id for your div, as per your information your text will be unique.
<div id="UniqueText" class="_className">UniqueText</div>
and the js code will be
function getClassNameWhereText(text){
var className = $('#'+text).attr('class');
console.log(className);
}
UPDATE : if you want to using contains
then you can do this,
function getClassNameWhereText(text){
var val = document.getElementById(text).value;
if(text.indexOf(val)>=0){
var className = $('#'+text).attr('class');
console.log(className);
}
}
This should be faster than using jQuery (but a bit more to type):
var xpath = "//div[text()='UniqueText']";
var result = document.evaluate(xpath,
document, null, XPathResult.FIRST_ORDERED_NODE_TYPE);
var node = result.singleNodeValue;
if (node) {
console.log(node.className);
} else {
console.error("Not found!");
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="_className">UniqueText</div>
The reason is, browser's CSS selectors don't support :contains selector, and jQuery needs to emulate it by checking every node matching the rest of the selector. Ditto for using .filter. But XPath is done natively by the browser.
You also cannot specify exact match using the jQuery :contains, like here. If substring matching was indeed needed, you can change the XPath:
var xpath = "//div[contains(text(),'UniqueText')]";
XPath is very powerful, but a bit finicky and largely unknown, so I find it is very under-utilised, even when its use would be a perfect fit.

wrap text with div excluding all children

I have got a problem which I have been trying to solve for past 2 days, but couldn't solve it.
Problem:
I have a HTML as
<div class="string-box">
Text 1 wrap me
<span class="inner-span">Don't search me</span>
<span class="inner-span">Identical text</span>
Identical text
substring match
<span class="inner-span">Don't search substring match</span>
<br>
Finally wrap me
</div>
I want to wrap the content has below
<div class="string-box">
<span> Text 1 wrap me </span>
<span class="inner-span">Don't search me</span>
<span class="inner-span">Identical text</span>
<span>Identical text</span>
<span>substring match</span>
<span class="inner-span">Don't search substring match</span>
<br>
<span>Finally wrap me</span>
</div>
What I have tried
a) I firstly tried to search for string in the html and used string replace to replace the string with wrapped string.
The problem with this approach is that it replaces things in child too..
b) I tried cloning the node and removing all child , but then I am lost with the position of the string.
c) I tried the following code to replace textNode, but I ended up getting HTML
$('.string-box').contents().filter(function(){
return this.nodeType == 3;
}).each(function(){
this.nodeValue = '<span>'+this.nodeValue+'</span>';
})
Thanks in advance for you time & help..
Your code is close, however the nodeValue cannot contain HTML - hence why it gets encoded in your attempt. Instead you can wrap() the textNode in HTML, like this:
$('.string-box').contents().filter(function() {
return this.nodeType == 3;
}).each(function() {
$(this).wrap('<span />');
})
Working example
Update
Here's a fix for occasions where the nodeValue contains a line break, and you want to wrap each line of the node in its own span:
$('.string-box').contents().filter(function() {
return this.nodeType == 3 && this.nodeValue.trim();
}).each(function() {
var $spans = this.nodeValue.trim().split(/\r?\n/).map(function(i, v) {
return $('<span />', { text: i + ' ' });
});
$(this).replaceWith($spans);
})
Working example
Just do it only in an each and you're fine. No need for filter the elements first. And you can use jQuery to wrap the content in a span.
$('.string-box').contents().each(function() {
if( this.nodeType == 3 )
$(this).wrap('<span />');
});
Working example.
As you already have a jQuery answer, allow me to offer a plain JavaScript alternative:
var stringBoxes = document.querySelectorAll('.string-box'),
stringBoxArray = Array.from(stringBoxes),
newElement = document.createElement('span'),
clone,
children;
stringBoxArray.forEach(function(box){
children = Array.from( box.childNodes ).filter(function(child){
return child.nodeType === 3;
}).forEach(function (text) {
clone = newElement.cloneNode();
text.parentNode.insertBefore(clone, text);
clone.appendChild(text);
});
});

How to replace contents of parent element, ignoring children using jQuery

I have some HTML like this:
<div id="demo">
<p>
<span class="myClass">Word test should not be marked</span>
Word test should be marked<br />
</p>
</div>
How can I find a word ('test') inside the div excluding the span and mark it using jQuery? I have seen a lot of solutions in SO, etc. but none of them worked for me. FYI the code I'm trying to use is something like this :
var regex = XRegExp('test', 'i');
$('#demo').markRegExp(regex);
Get all child nodes using contents() and then iterate and update the text nodes.
$('#demo p')
.contents() // get all child nodes including text and comment nodes
.each(function() { // iterate over nodes
if (this.nodeType == 3) // check node is text node
$(this).replaceWith(this.textContent.replace(/test/g, '<span class="test">$&</span>')) // update the content and replace node with html content
});
.test {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="demo">
<p>
<span class="myClass">Word test should not be marked</span>
Word test should be marked
<br />
</p>
</div>
Here's a fiddly regex solution:
var html=$("#demo").html();
var elements=html.split(/(<[^>]+>)/); // split into tags and text
$.each(elements,function(i,e){
if(!e.match(/<[^>]+>/)){
if(elements[i-1]!==undefined){
if(!$(elements[i-1]).hasClass('myClass')){
elements[i]=elements[i].replace(/test/g,"<span class='highlight'>test</span>");
}
}
}
});
$("#demo").html(elements.join(''));
It would be much cleaner, though, to mark the text you do want to replace with a class, then you could just do:
$("#demo").find("span[class='myClass']").each(function(){
$(this).html($(this).text().replace(/test/g,"<span class='highlight'>test</span>"));
});
Working JSFiddle for both solutions.

jquery: separate tag and text

I have:
// $(this): <span class="class1"><span class="class2"></span> Some text</span>
$(this).children().each(function () {
console.log(this);
});
from the console I got only the span with class2, How I can get also the text? something like:
['<span class="class2"></span>', 'Some text']
$(this).children() will only return child nodes that are elements.
You'll want to use $(this).contents() to get all the nodes, including text nodes. :)
You can then filter these nodes to just get elements and text (see node types):
$(this).contents().filter(function() {
return (this.nodeType === 1) || (this.nodeType === 3);
})
notice the diference between .contents() and .children(). You require the former
While .children() return all available elements, .contents also return text and comments
you can get your output like this
jQuery(".class1").contents()

jQuery comment/uncomment <!--element-->

I am looking for a way to wrap, with jQuery, an element into a comment, like:
<!--
<div class="my_element"></div>
-->
and also a way to remove the comments.
Is this possible?
To wrap an element with comment, or more specifically to replace an element with a comment node having that element's HTML:
my_element_jq = $('.my_element');
comment = document.createComment(my_element_jq.get(0).outerHTML);
my_element_jq.replaceWith(comment);
To switch it back:
$(comment).replaceWith(comment.nodeValue);
If you don't have the reference to the comment node then you need to traverse the DOM tree and check nodeType of each node. If its value is 8 then it is a comment.
For example:
<div id="foo">
<div>bar</div>
<!-- <div>hello world!</div> -->
<div>bar</div>
</div>
JavaScript:
// .contents() returns the children of each element in the set of matched elements,
// including text and comment nodes.
$("#foo").contents().each(function(index, node) {
if (node.nodeType == 8) {
// node is a comment
$(node).replaceWith(node.nodeValue);
}
});
You can comment the element out by doing the following:
function comment(element){
element.wrap(function() {
return '<!--' + this.outerHTML + '"-->';
});
}
DEMO:
http://jsfiddle.net/dirtyd77/THBpD/27/
I'm impresed nobody gave the following solution. The following solution require a container. This container will have inside, the commented / uncommented code.
function comment(element) {
element.html('<!--' + element.html() + '-->')
}
function uncomment(element) {
element.html(element.html().substring(4, element.html().length - 3))
}
function isCommented(element) {
return element.html().substring(0, 4) == '<!--';
}
Example: https://jsfiddle.net/ConsoleTVs/r6bm5nhz/
For wrapping?
function wrap(jQueryElement){
jQueryElement.before("<!--").after("-->");
}
Not sure how successful you'd be finding the comments once wrapped though. A text search on the body element using regular expressions is an option.
Or this - is it possible to remove an html comment from dom using jquery

Categories

Resources