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()
Related
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>
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);
});
});
I have a paragraph like so:
<p>
<strong>Cost:</strong>
$1,500 - $10,000 (see below for details)
</p>
where I want to select first the strong text and then the not strong text.
I was trying the following:
$('p').not('strong').html()
and
$('p:not("strong")')
but it doesn't seem to be working.
If you need p elements that do not has strong in them, then you need to combine :has selector as well
$('p:not(:has(strong))')
for getting the text node text without strong contents:
$('p').clone().children().remove().end().text();//returns $1,500 - $10,000 (see below for details)
for getting the strong element text:
$('p strong').text();//return Cost:
Working Demo
Try this:
$("p").clone().children().remove().end().text();
Try
var elems = $.parseHTML($("p").html()).filter(function(elem, i) {
return elem.tagName === "STRONG"
|| elem.previousSibling !== null
&& elem.previousSibling.tagName === "STRONG"
});
// `elems[0]`:`STRONG` element , `elems[1]`:`#text` node;
// `$(elems).eq(0)`:jQuery object `$("strong")`,
// `$(elems).eq(1)`:jQuery object `$("text")`
console.log(
elems[0], elems[1],
$(elems).eq(0).text(), $(elems).eq(1).text()
);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<p>
<strong>Cost:</strong>
$1,500 - $10,000 (see below for details)
</p>
let's assume I have this code
<p>FirstLevelP
<span>SecondLevelSpan</span>
</p>
<p>FirstLevelP
<span>SecondLevelSpan
<p>ThirdLevelP</p>
</span>
</p>
Is it possible to iterate through every element that I have right now, but only get the content, that's in the direct node of it, modify the text and then have it in the original content?
Example, If I go through every $('p').each and would extract the text I would also get the text inside the span.
Basically this:
FirstelElement: FirstLevelPSecondLevelSpan
SecondElement: SecondLevelSpanSecondLevelSpanThirdLevelP
But I want to have it like this
FirstelElement: FirstLevelP
SecondElement: SecondLevelSpan
ThirdElement: FirstLevelP
FourthElement: SecondLevelSpan
FifthElement: ThirdLevelP
Is this possible?
In my research I already found this answer here
$("#foo")
.clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.text();
But this would only solve half of my problems. I would still need to modify the text in the original content! Thanks in advance guys.
EDIT FOR CLARIFICATION
So basically, want I want to achieve is something like this:
For every element, I want to check if there is a dot at the end. If not I want to add one. I already managed to do this for headlines, like this:
foreach (pq($content)->filter(':header') as $headline) {
if (substr(pq($headline)->text(), 0, -1) != '.') {
$content = preg_replace('#(' . pq($headline) . ')#', pq($headline) . '.', pq($content));
}
}
The problem, as I stated, is, that when I have nested elements it would add the dot after the whole element, and not after each sub element if neccessary.
To work with my "assumed" code, it should look like this
<p>FirstLevelP.
<span>SecondLevelSpan.</span>
</p>
<p>FirstLevelP.
<span>SecondLevelSpan.
<p>ThirdLevelP.</p>
</span>
</p>
But unfortunatley, it currently looks like this
<p>FirstLevelP
<span>SecondLevelSpan</span>.
</p>
<p>FirstLevelP
<span>SecondLevelSpan
<p>ThirdLevelP</p>
</span>.
</p>
Note the dots.
finding and changing text without child elements works this ways:
// search every element
$("body *").each(function(index, el) {
// find first text node
var node = $(el).contents().filter(function() {
return this.nodeType === 3;
})[0];
// change text
node.textContent = "new text";
});
Edit, Updated
Try
$("body *").each(function (i, el) {
if ($(el).is("p, span")) {
$(el).text(function (idx, text) {
var t = text.split("\n")[0];
// if `text` string's last character is not `.`
// concat `.` to `text` string ,
// return `text` original string's with `.` added
return t.slice(-1) !== "." ? t + "." : t
})
}
})
$("body *").each(function (i, el) {
if ($(el).is("p, span")) {
$(el).text(function (idx, text) {
var t = text.split("\n")[0];
return t.slice(-1) !== "." ? t + "." : t
})
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<p>FirstLevelP
<span>SecondLevelSpan</span>
</p>
<p>FirstLevelP
<span>SecondLevelSpan
<p>ThirdLevelP</p>
</span>
</p>
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