Combining TextContent and information from elements with jQuery - javascript

How can I combine text content and content of elements with jQuery?
function getText(key, node) {
if (node.tagName == 'INPUT') {
return node.value;
} else {
return node.textContent;
}
}
console.log($("div").find('*').map(getText).get().join(' ').trim().replace(/\s+/g, ' '));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<span>A</span>
<div>
B
<input value='4'>
<span>C</span>
</div>
</div>
should be rendered to the string "A B 4 C".
It is no problem to get just the textContent with .text() or render the values of the input elements with .map(function(){return $(this).val();}).get().join(' ') but getting both combined in the right order is difficult.
EDIT: The implementation should work on every depth of nesting elements. It is used to get a comparable output for unit testing parts of a web page where some visible texts like labels are rendered into span and other parts like editable values are in input elements.

Depending on what you want to do with it you can just loop through all the elements in the top level div ignoring the text of child elements and append it to where you want it getting the text or value. Something like this
$(".top *").each(function(){
$(".holder").append($(this).clone().children().remove().end().text() || $(this).val());
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="top">
<span>A</span>
<div>
B
<input value='4'>
<span>C</span>
</div>
</div>
<div class="holder">
</div>
NOTE: I added a class of top to the top level div to be able to select it

One way to do it is just to coalesce the values using an OR if the val() is something use it, otherwise get the text().
$(function() {
var a = [];
a = $.map($(".read *"), function(elem) {
return $(elem).val() ||
$(elem)[0].childNodes[0].nodeValue.trim();
});
console.log(a.join(" "));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='read'>
<span>A</span>
<div>
B
<input value='4'>
<span>C</span>
</div>
</div>

With the ideas of the others I created this answer. Perhaps it could be optimized in some way, but at least it works like expected.
function getText(key, node) {
if (node.tagName == 'INPUT') {
return node.value;
} else {
let result = "";
for(var child=node.firstChild; child!==null; child=child.nextSibling) {
if (child.nodeType === 3) {
result = result + " "+ child.nodeValue;
}
}
return result;
}
}
console.log($("div").find('*').map(getText).get().join(' ').trim().replace(/\s+/g, ' '));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<span>A</span>
<div>
B
<input value='4'>
<span>C</span>
</div>
</div>
EDIT: In fact it also doesn't work correctly if C is not inside of a span.

Related

shorter way to check if a title exists

Checking if a title already exists. Is there a shorter way? Something like:
if(test.exists.inside('.title'){...
var x = 0;
var test = $('#test').text();
$('.title').each(function(){
if($(this).text() == test){x = 1;}
});
if(x == 1){console.log('exists');}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='title'>lorem</div>
<div class='title'>ipsum</div>
<div class='title'>lorema</div>
<div class='title'>ipsuma</div>
<div id='test'>lorem</div>
a lot of ways to do this :contains , filter() with indexOf() and filter() with text equal test.. depending on what you're trying to do
var x = 0;
var test = $('#test').text();
var Exists = $('.title:contains("'+test+'")').length;
console.log('Yes '+Exists+ ' title with text '+test);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='title'>lorem</div>
<div class='title'>ipsum</div>
<div class='title'>lorema</div>
<div class='title'>ipsuma</div>
<div id='test'>lorem</div>
you can check also with for contains text check
$('.title:contains("'+test+'")').length > 0
OR for exact text check
$('.title').filter(function(){
return $(this).text().trim() == test;
}).length > 0
Note: :contains and .indexOf search for the text contains text not the exact text .. by using $(this).text().trim() == test; this
will return the exact text element
If you're just looking for shorter code, one option would be to invoke Array.prototype.some, and rather than store the result of .text() in a variable, invoke it every time, to save on characters typed:
if ([].some.call($('.title'), d => $(d).text() === $('#test').text())) {
console.log('exists');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='title'>lorem</div>
<div class='title'>ipsum</div>
<div class='title'>lorema</div>
<div class='title'>ipsuma</div>
<div id='test'>lorem</div>
Personally, I'd prefer readability over code length:
const textToFind = $('#test').text();
const exists = Array.prototype.some.call(
$('.title'),
title => $(title).text() === textToFind
);
if (exists) {
console.log('exists');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='title'>lorem</div>
<div class='title'>ipsum</div>
<div class='title'>lorema</div>
<div class='title'>ipsuma</div>
<div id='test'>lorem</div>

Replacing indexes on each element and its content

I need to replace a text recurring multiple times in several elements with the same id, both in the its tag and its inner html, with a progressive number. Example:
<div id="element_x_">
<p>Line number _x_</p>
</div>
<div id="element_x_">
<p>Line number _x_</p>
</div>
Should become:
<div id="element1">
<p>Line number 1</p>
</div>
<div id="element2">
<p>Line number 2</p>
</div>
What I tried so far is this:
$('#element_x_').each(function(i) {
$(this).contents($(this).contents.replace(/_x_/g, i));
});
But that's just not working, I guess the .contents function is not what I need and I'm not sure how to select the entire element (tag + content) of each iteration for the replace to do its job.
Note that you're starting with an invalid document, since id values must be unique. So you have no guarantee that the browser will retain the invalid id after parsing. In practice, I've never met one that didn't, but I'd still remove those duplicate ids and give the relevant elements a shared class instead.
If you do that, then
$(".the-class").each(function(i) {
this.id = "element" +i;
this.innerHTML = this.innerHTML.replace(/_x_/g, i);
});
$(".the-class").each(function(i) {
this.id = "element" + i;
this.innerHTML = this.innerHTML.replace(/_x_/g, i);
});
<div class="the-class">
<p>Line number _x_</p>
</div>
<div class="the-class">
<p>Line number _x_</p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
If you don't do that, you could use an attribute selector:
$("[id=element_x_]").each(function(i) {
this.id = "element" +i;
this.innerHTML = this.innerHTML.replace(/_x_/g, i);
});
$("[id=element_x_]").each(function(i) {
this.id = "element" + i;
this.innerHTML = this.innerHTML.replace(/_x_/g, i);
});
<div id="element_x_">
<p>Line number _x_</p>
</div>
<div id="element_x_">
<p>Line number _x_</p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Which works with the current jQuery and major browsers. But I wouldn't.
Note that in both cases, we're assuming you want all _x_ inside these elements to be replaced with the same i value.
A note about this.innerHTML = this.innerHTML.replace(/_x_/g, i);: While quick and easy, it will also tear down all of the elements inside the element you do it on, throwing away any event handlers on them, and then make the browser recreate them by parsing the new HTML. It will also look for _x_ everywhere, not just in the text. That may be what you want, of course.
Having done this.id = ... to update the id, if you only want to process the text and not tear down recreate elements, jQuery actually makes that fairly easy with its contents function, but we need to recurse:
$(".the-class").each(function(i) {
this.id = "element" + i;
updateElement(this, /_x_/g, i);
});
function updateElement(element, rex, rep) {
$(element).contents().each(function() {
switch (this.nodeType) {
case 1: // Element
updateElement(this, rex, rep);
break;
case 3: // Text node
this.nodeValue = this.nodeValue.replace(rex, rep);
break;
}
});
}
$(".the-class").each(function(i) {
this.id = "element" + i;
updateElement(this, /_x_/g, i);
});
function updateElement(element, rex, rep) {
$(element).contents().each(function() {
switch (this.nodeType) {
case 1: // Element
updateElement(this, rex, rep);
break;
case 3: // Text node
this.nodeValue = this.nodeValue.replace(rex, rep);
break;
}
});
}
<div class="the-class">
<p>Line number _x_</p>
</div>
<div class="the-class">
<p>Line number _x_ <span>Nested _x_</span></p>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
You need to replace the current element with the replaced content. But note that this is not a good design - because ID of an element must be unique and any event handlers/data attached to those elements will be lost by doing replacement
$('div[id="element_x_"]').each(function(i) {
$(this).replaceWith(this.outerHTML.replace(/_x_/g, i + 1));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="element_x_">
<p>Line number _x_</p>
</div>
<div id="element_x_">
<p>Line number _x_</p>
</div>
Change from id to class id must be unique your can do it by class.
<div class="element_x_">
<p>Line number _x_</p>
</div>
<div class="element_x_">
<p>Line number _x_</p>
</div>
<script type="text/javascript">
$('.element_x_').each(function(i) {
$(this).html($(this).html().replace(/_x_/g, i+1));
});
</script>
On other way, handling attribute id and p text:
$('[id=element_x_]').attr('id', function(index) {
var i = ++index;
$(this).children('p').text(function(_, txt){
return txt.replace('_x_', i)
})
return "element" + i;
});
That's said, having to doing it client side seems really wrong. This should be handled server side, before rendering the HTML markup.
You should not loop over and ID as it is supposed to be unique. You need to add a class, or select your elements by type (div)
$('.elt_x').each(function(i) {
var thisElt = $(this);
var thisChild = thisElt.children("p");
thisElt.attr("id", thisElt.attr("id").replace("_x_", i+1)).removeClass("elt_x");
thisChild.text(thisChild.text().replace("_x_", i+1));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="element_x_" class="elt_x">
<p>Line number _x_</p>
</div>
<div id="element_x_" class="elt_x">
<p>Line number _x_</p>
</div>

Javascript increment through elements within div?

Ok, so I have the following HTML:
<div class="calculator-section">
<p class="x"></p>
<p class="y"></p>
</div>
<div class="calculator-section">
<p class="z"></p>
<p class="a"></p>
</div>
<div class="calculator-section">
<p class="b"></p>
<p class="c"></p>
</div>
I need to increment through each of these divs and compare classes that each <p> has.
How would I go about doing this?
Currently I have this:
$('.calculator-section').each(function(i, obj) {
$(this).$('p').each(function(i, obj) { //This bit doesn't work
//Check classes for $(this) here?
});
});
But I'm not sure what to do for that inner loop. Any ideas?
Add p to your initial each loop and use the className property within the loop
$('.calculator-section p').each(function(i, obj) {
if(this.className == "x") {
$(this).css('background', 'green')
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="calculator-section">
<p class="x">x</p>
<p class="y">y</p>
</div>
Alternatively, if you are using the multiple loops for a reason:
Select p within the iteration of specific section with $('p', this) or $(this).children('p') etc
$('.calculator-section').each(function(i, obj) {
// $(this) = section
$('p', this).each(function(i, obj) {
// $(this) = p within above section
});
});
Use single each() instead of double each(). Example here..
$('.calculator-section p').each(function(i, obj) {
var className = $(this).attr('class');
if(className == 'expected value'){
//do something
//return false;
}
});
You're going to get too many results. <p class="x"><p> should be <p class="x"></p> (with a forward slash to indicate closing the paragraph.
<div class="calculator-section">
<p class="x"></p>
<p class="y"></p>
</div>
<div class="calculator-section">
<p class="z"></p>
<p class="a"></p>
</div>
<div class="calculator-section">
<p class="b"></p>
<p class="c"></p>
</div>
Once that's fixed,
//you can grab the class couples this way
var results = $('.calculator-section').map(function(i, obj) {
return $(obj).find('p').map(function(i, obj) {
return obj.className;
});
});
//and then do what you want with them later
results.each(function(i, obj) {
console.log(obj[0] + ',' + obj[1]);
});
>> x,y
>> z,a
>> b,c

Check Div Elements by name fast

I have some Divs:
<div id="content">
<div class="c" id="1">
<div id="xyz">dont care</div>
<div id="texts1">
<div name="check"> ContentText </div>
</div>
</div>
<div class="c" id="2">
<div id="xuyz">dont care</div>
<div id="texts2">
<div name="check"> ContentText </div>
</div>
</div>
</div>
I want to iterate through all elements of the "c" class.
Then I want to check, if the Div elements named "check" of each "c" element contains special text.
If true, then manipulate the "c" element (which contains the special text)
I tried something like this:
var ele = document.getElementsByClassName("c");
for(var i=0;i<ele.length;i++)
{
var check = ele[i].getElementsByName("check");
if(check.innerHTML ....)
}
But thats not working :/
Log from Firefox:
TypeError: ele[i].getElementsByName is not a function
Where is my mistake?
A simple querySelectorAll() should do the trick:
var check = document.querySelectorAll('.c [name="check"]');
And as stated in a comment already, only document has getElementsByName method.
With jQuery this is very simple -
$('[name="check"]:contains("your special text")')
With jQuery (you have tagged it with it as well)
$(document).ready(function () {
$('div.c').find('div[name="check"]').each(function(){
// here check HTML and do needed manipulations
if($(this).html() == 'ContentText'){
$(this).closest('div.c').children().first().html('I CARE');
}
});
});
see jSFiddle -> http://jsfiddle.net/ApfJz/32/
Here is a modification of your code to make it work as intended
var ele = document.getElementsByClassName("c");
for (var i = 0; i < ele.length; i++)
{
if (ele[i].getAttribute('name') === "check") {
// do something with matching elements here
}
}

How to use jquery prepend() function for adding rupee symbol before all numbers

Here i am trying to add Rupee Symbol before the numbers. If there is "-" then i don't want to add the Rupee symbol over there. here is my code here
var te = $('p.active').text();
var te2 = $('p.in_active').text();
if(te === '-'){}
else{$('p.active').prepend("<span class='WebRupee'>Rs.</span>");}
if(te2 === '-'){}
else{$('p.in_active').prepend("<span class='WebRupee'>Rs.</span>");}
My Html Is
<link rel="stylesheet" type="text/css" href="http://cdn.webrupee.com/font" />
<div class="main">
<p class="active">10,200</p>
<p class="in_active">1,68,10,900</p>
<p class="active">0</p>
<p class="in_active">-</p>
<p class="active">12,78,200</p>
<p class="in_active">-</p>
<p class="active">-</p>
<p class="in_active">9,890</p>
<p class="active">10,200</p>
<p class="in_active">1,68,10,900</p>
<p class="active">0</p>
<p class="in_active">-</p>
<p class="active">12,78,200</p>
<p class="in_active">-</p>
<p class="active">-</p>
<p class="in_active">9,890</p>
</div>​
my css is
.in_active{color:#999;font-size:11pt;font-weight:400;}
.active{color:#333;font-size:11pt;font-weight:400;}
.main{padding:30px;}​
It would make sense to loop through the paragraphs instead. How about:
$('.active, .in_active').each(function() {
if ( $(this).text() != '-' ) {
$(this).prepend('<span class="WebRupee">Rs.</span>');
}
});
$('p.active').prepend("<span class='WebRupee'>Rs.</span>"); selects every active paragraph and prepends the Rs. symbol. You want to use a loop instead, which checks for every paragraph whether the prepend should take place or not. For example, here's what you could do:
$('p.active, p.in_active').each(function() {
if($(this).text() !== '-') {
$(this).prepend("<span class='WebRupee'>Rs.</span>");
}
});
Also note that you can have several classes per element, which means that you can have one single classe for every <p> in your exemple: <p class="rupee in_active">1,68,10,900</p> versus <p class="active">10,200</p>. If all elements with the "rupee" class should have the Rupee symbol prepended, then you can simply write:
$('p.rupee').prepend("<span class='WebRupee'>Rs.</span>");
$('.in_active').each(function(){
var hyphen = $(this).html();
if(hyphen == '-'){
}else{
$(this).prepend("<span class='WebRupee'>Rs.</span>");
}
});
Please check this working example fiddle
$(document).ready(function(){
$(".main p").each(function(){
var pValue = $(this).text();
if(pValue != '-'){
$(this).prepend("<span class='WebRupee'>Rs.</span>");
}
});
});

Categories

Resources