Replacing indexes on each element and its content - javascript

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>

Related

Wrap two elements in HTML with javascript

I have this code that is generated by php:
<div class="fusion-post-content post-content">
<h2 class="blog-shortcode-post-title"></h2>
<p class="fusion-single-line-meta"></p>
<div class="fusion-post-content-container"></div>
</div>
I need to wrap two elements by using javascript so the code would look like this:
<div class="fusion-post-content post-content">
<div class="class">
<h2 class="blog-shortcode-post-title"></h2>
<p class="fusion-single-line-meta"></p>
</div>
<div class="fusion-post-content-container"></div>
</div>
This will do what you want. Isn't it better to change the code on server-side??
// Select the first element found
var parent = document.querySelector('.fusion-post-content');
console.log('Old child-length', parent.children.length);
console.log('Old:', parent.innerHTML);
// *You don't need the timeout
setTimeout(function () {
var h2 = parent.firstElementChild;
var p = parent.firstElementChild.nextElementSibling;
// Remove cildren
parent.removeChild(h2);
parent.removeChild(p);
// Insert the new child
parent.insertAdjacentHTML('afterbegin', '<div class="class"></div>');
// Insert the other children (old) in the new child
parent.firstElementChild.insertAdjacentElement('beforeend', h2);
parent.firstElementChild.insertAdjacentElement('beforeend', p);
// Gets one less, since we put to children in one (3 - 1 = 2)
console.log('New child-length', parent.children.length);
console.log('New:', parent.innerHTML);
}, 500);
<div class="fusion-post-content post-content">
<h2 class="blog-shortcode-post-title"></h2>
<p class="fusion-single-line-meta"></p>
<div class="fusion-post-content-container"></div>
</div>

Combining TextContent and information from elements with jQuery

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.

How to target Elements specifically in DOM

<html>
</head>
<body>
<div class="class1">
<p>text to change1</p>
<p>text to change2</p>
<p> </p>
</div>
<div class="class2">
<p>text to change1</p>
<p>text to change2</p>
<p> </p>
</div>
<button onclick="obj1.changehtml()">changehtml</button>
<script type="text/javascript">
var change= function()
{
}
change.prototype.changehtml=function()
{
// targeting specific elements positions
var paragraph=document.getElementsByClassName("class1");
paragraph[0].innerHTML="hello";
paragraph[1].innerHTML="hell1";
var addthem = paragraph[2].innerHTML = second;
}
var obj1= new change;
</script>
</body>
In the above program I am facing an error
Uncaught TypeError: Cannot set property 'innerHTML' of undefined
when trying to change the second paragraph[1].innerHTML element in first class(Class1) using property.
How do I proceed here?
in your code you were missing [0].children after getting the `.class1' elements. Also you should note this returns a NodeList of matched elements not individial elements.
If you have used jQuery before this can make you feel that this would be the native javascript functionality allowing you to chain dom methods to the NodeList but unfortunately it is not.
var change = function(){}
change.prototype.changehtml = function(){
// targeting specific elements positions
// okay selector
// var paragraphs = document.getElementsByClassName("class1")[0].children;
// okayer selector
// var paragraphs = document.getElementsByClassName('class1')[0].getElementsByTagName('p')
// better more explicit selector
var paragraphs = document.querySelectorAll('.class1 > p');
console.log( paragraphs )
paragraphs[0].innerHTML="hello";
paragraphs[1].innerHTML="hello1";
// second is not defined
var addthem = (paragraphs[2].innerHTML = second);
}
var obj1= new change;
<script src="http://codepen.io/synthet1c/pen/WrQapG.js"></script>
<div class="class1">
<p>text to change1</p>
<p>text to change2</p>
<p> </p>
</div>
<div class="class2">
<p>text to change1</p>
<p>text to change2</p>
<p> </p>
</div>
<button onclick="obj1.changehtml()">changehtml</button>

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

Categories

Resources