wrap text with div excluding all children - javascript

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);
});
});

Related

Find exact string in element jQuery

I have a couple of span elements inside a container and I want to match the exact word of one of the spans to insert a banner. I cant figure out how to do this.
First i tried this script:
$(document).ready(function(){
if ($('.head.titlebox span').text().trim() === "match" ){
$('<span class="myAwesomeBanner4"></span>').insertAfter(".productbox em.price.product-card-price")
}
else if ($('.head.titlebox span').text().trim() === "matchother" ){
$('<span class="myAwesomeBanner5"></span>').insertAfter(".productbox em.price.product-card-price")
}
});
This doesnt work - unless I remove the string it should match: === "". So the script seems kike it kinda works. I cant match it to the words though - looks like its correct to me, so not sure why it's not working.
Then I tried this script which works - but I cant figure out how to convert it to use if statement and to create my div to insert in the DOM like above:
$('.head.titlebox span').filter(function(index) {
return $(this).text() === "match";}).css("background", "black");
My HTML for targeting the string:
<div class="head titlebox">
<span id="artid">text</span><h1 id="prod-title">text</h1>
<span>text</span><span>text</span>
<span>match</span>
</div>
So why is the first not working - and how do I combine it with the working filter function of the second script?
You need to loop through all the span tags,
$('.head.titlebox span').each(function() {
if ($(this).text() == "match") {
//insert your html.
}
})
try this:
$(".head").on("click", function() {
var spanList = $(".head span");
$.each(spanList,function(span){
console.log($(spanList[span]).text());
if($(spanList[span]).text() === "match"){
console.log("Matched")
}
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="head titlebox">
<span id="artid">text</span>
<h1 id="prod-title">text</h1>
<span>text</span><span>text</span>
<span>match</span>
</div>

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()

Iterate over every element and get only the content that is directly in the node

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>

Using javascript to remove the last instance of a specific HTML entity (breadcrumb)

I need to remove the last instance of
>
from:
<b>
Home >
Category >
Sub-category >
Sub-sub-category >
</b>
I assume regex will need to be employed, but I haven't found any good ways to do it. I'm removing the last instance of the link with
(a:last).remove();
but, after a couple iterations, I get multiple > > > in a row with no links between
How about a solution in CSS instead of javascript?
CSS
.link {
display: inline-block;
}
.link:after {
content: ' \003e';
}
.link:last-of-type:after {
content: '';
}
HTML
<div class="link"> Crumb 1 </div>
<div class="link"> Crumb 2 </div>
<div class="link"> Crumb 3 </div>
see http://jsfiddle.net/stackolee/tzbDe/
Could you wrap ... > in a span like so: <span>... ></span> and then do (span:last).remove();?
It would lead to a cleaner solution with little extra markup.
If you can edit the markup, I'd recomment khalid13's answer. Otherwise, you could try showdev's comment to select a text node or hack through it like this:
//where b is your `<b>` element:
var links = b.innerHTML.split('>')
links.pop()
b.innerHTML = links.join('>')
What I'm doing here is splitting the menu into a JS array based on the '>' characters, then removing the last element of the array and joining it all back together again.
If you really want to do this in plain javascript, you can do this:
// pass the node before where you want to look for the ">"
function removeTrailingGt(start) {
var node = start.nextSibling;
while (node && node.nodeType === 3) {
if (node.nodeValue.indexOf(">") !== -1) {
node.nodeValue = node.nodeValue.replace(">", "");
break;
}
node = node.nextSibling;
}
}
It starts from the last link in your containing object and then searches all following text nodes and removes the first ">" that it finds.
Working demo: http://jsfiddle.net/jfriend00/BC647/

How to select an item that does not contain a class/id - jquery

I have been trying to get the selector that matches the following:
Title > Prints > Ginger
How do i select "Ginger" via jquery
code for the above are:
<span class="selectednav">
Title
<span class="navigation-pipe"> > </span>
Prints
<span class="navigation-pipe"> > </span>
Ginger
</span>
Any help would much be appreciated
Thanks
full edit sorry first answer not adapted // although not sure this works to be honest
$('.selectednav').contents().filter(
function() { if (this.nodeType == 3) $(this).wrap("<span class='orphan'></span>"); });
$orphan = $('.selectednav').find(".orphan");
edit in one line for perf sake :
$orphan = $('.selectednav').contents().filter(
function() { if (this.nodeType == 3) $(this).wrap("<span class='orphan'></span>"); }).end().find(".orphan");
also each would work instead of filter
then if only to get the text this should do
orphantext= $('.selectednav').contents().filter(
function() { return this.nodeType == 3; })
.text();
Something like $(".selectednav").find(".navigation-pipe").last() should work.
This Link might be what your looking for
Transversing
I think the code may look like this if you have a div around the code you have presented
$("div span").last()

Categories

Resources