I have some content that is loaded from a XML file with XMLHttpRequest:
<div uid="29710" >
content....
</div>
<div uid="19291" >
content....
</div>
<div uid="099181" >
content....
</div>
The code used to get the content:
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","notes.xml",false);
xmlhttp.send();
xmlDoc = xmlhttp.responseXML;
var x = xmlDoc.getElementsByTagName("noteboard");
var newHTML = "";
for (i=0;i<x.length;i++) {
newHTML += "<div><div uid='"+ x[i].getAttribute('uid')+"'>"
+ x[i].getElementsByTagName("title")[0].childNodes[0].nodeValue
+ "<br>"+ x[i].getElementsByTagName("message")[0].childNodes[0].nodeValue
+"</div></div>";
}
document.querySelector('#panel').innerHTML += newHTML;
The content is updated every 3 seconds, but the problem starts when the old content is repeated and piled up, as well as the new content. Also, the UID attribute value is loaded from the same XML file as the content - so all the new repeated content has the same UID as the old repeated content.
If I am not clear enough, this is the sort of thing that is happening:
<div uid="19291">Old Content</div>
<div uid="77191">New Content</div>
<div uid="19291">Old Content</div>
<div uid="19291">Old Content</div>
<div uid="21503">New Content</div>
So how would you remove all the divs that have the same UID value as each other, except for the original one ?
You could check if the uid you are adding already exists in the page before adding it.
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","notes.xml",false);
xmlhttp.send();
xmlDoc = xmlhttp.responseXML;
var x = xmlDoc.getElementsByTagName("noteboard");
var newHTML = "";
for (i=0;i<x.length;i++) {
// check if the UID exists in the page and only add it if it does not
var uid = x[i].getAttribute('uid');
if ( !document.querySelector('#panel *[uid="'+ uid +'"]') ){
newHTML += "<div><div uid='"+ x[i].getAttribute('uid')+"'>"
+ x[i].getElementsByTagName("title")[0].childNodes[0].nodeValue
+ "<br>"+ x[i].getElementsByTagName("message")[0].childNodes[0].nodeValue
+ "</div></div>";
}
}
document.querySelector('#panel').innerHTML += newHTML;
One option (using "vanilla javascript", no jQuery), if you are sure that the correct element is first in the DOM, is to use document.querySelectorAll to loop through the elements, removing each of them except the first one. For instance, if you had a <div> with an ID of panel and you wanted to remove divs with a duplicate UID (using 11111 as an example) from inside of this container:
var divs = document.querySelectorAll('div[uid="11111"]');
Array.prototype.forEach.call(divs, function(el, idx) {
if(idx > 0) {
document.getElementById('panel').removeChild(el);
}
});
Array.prototype is used because document.querySelectorAll returns an array-like NodeList, not an actual Array.
(Update following OP's update: this would go after the data has been retrieved from the XML file.)
You can see if your document already has a div with that uid.
var div = document.querySelector('div[uid=' + items[i].getAttribute('uid') + ']');
The code above will search the document any div that has an attribute uid with the same value.
So, your final code will be something like:
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function(e) {
var items = xhr.responseXML.getElementsByTagName("noteboard");
for (var i = 0; i < items.length; i++) {
var div = document.querySelector('div[uid=' + items[i].getAttribute('uid') + ']');
var newHTML = '';
if (!div) { // it doesn't exist yet
newHTML += "<div><div uid='"+ x[i].getAttribute('uid')+"'>"
+ x[i].getElementsByTagName("title")[0].childNodes[0].nodeValue
+ "<br>"+ x[i].getElementsByTagName("message")[0].childNodes[0].nodeValue
+"</div></div>";
}
}
document.getElementById('panel').innerHTML += newHTML;
});
xhr.open("GET", "notes.xml", false);
xhr.send();
Related
I generate with a loop for every section on my html site a list element.
<section class="page1" id="name1"></section>
<section class="page2" id="name2"></section>
<section class="page3" id="name3"></section>`
In my jQuery function, see below, I create for every section a link.
for( var i = 0; i < sections.length; i++){
_addClass(sections[i], "ops-section")
sections[i].dataset.index = i + 1;
sections[i].id=document.getElementById(sections[i].id);
if(settings.pagination == true) {
paginationList += '<li><a data-index="'
+ (i + 1) + '" href="#' + (i + 1)
+ '"></a><p class="lead">'
+ sections[i].id + '</p></li>';
}
with sections[i].id=document.getElementById(sections[i].id); I want to read out the text behind id, for example: name1. name2, name3 and so on. I want to add the id-name then as text between the p-tag, so that I get the following list element:
<li><a data-index="1" href="#1" class="active"></a><p class="lead">name1</p></li>
but actually I get this:
<li><a data-index="1" href="#1" class="active"></a><p class="lead">[object HTMLElement]</p></li>
Where is my mistake? What's wrong?
I think you are going about this the wrong way and making the code harder to follow in the process. Your issue is that you are concatenating an entire DOM node, rather than a value of one of the attributes of that node because of this line:
sections[i].id = document.getElementById(sections[i].id)
.getElementById() returns a DOM node so later, when you use:
sections[i].id
You aren't referring to the id at all, you are referring to the entire element returned from:
document.getElementById(sections[i].id)
You don't really even need any of that entire line anyway.
If you use a .forEach() loop to enumerate the section elements, you won't have to set up or manage a counter.
If you create the elements via the DOM API (instead of building a string), you can configure each element much more simply and get out of concatenation hell.
Look at the solution below, it's a little more overall code than your solution, but it is so much cleaner and easier to follow.
// Get the section elements into an array
var theSections = Array.prototype.slice.call(document.querySelectorAll("section[class^='page']"));
// Loop over the elements in the array
theSections.forEach(function(section, index){
// Create li, a and p elements
var li = document.createElement("li");
var a = document.createElement("a");
var p = document.createElement("p");
// Configure each new element
a.setAttribute("data-index", index + 1);
a.href = index + 1;
a.classList.add("active");
p.classList.add("lead");
p.textContent = section.id;
// Inject new elements into the DOM
li.appendChild(a);
li.appendChild(p);
document.body.appendChild(li);
// Just for testing
console.log(a, p);
});
<section class="page1" id="name1"></section>
<section class="page2" id="name2"></section>
<section class="page3" id="name3"></section>
Why is it not working?
First, document.getElementById retrieves an HTML element. Then, you are overriding the id in sections[i].id with the HTML element, resulting in [object HTMLElement].
Solution
As suggested by Liora Haydont, simply remove the line sections[i].id=document.getElementById(sections[i].id);.
for( var i = 0; i < sections.length; i++){
_addClass(sections[i], "ops-section")
sections[i].dataset.index = i + 1;
if(settings.pagination == true) {
paginationList += '<li><a data-index="'
+ (i + 1) + '" href="#' + (i + 1)
+ '"></a><p class="lead">'
+ sections[i].id + '</p></li>';
}
In your code you're attaching an entire HTML element to the section id which is why you're getting that error. Scott just beat me with his answer, but I'm in agreement with him. Using forEach will allow you to make your life a little easier.
In this example I'm also using template literals to create the HTML. YMMV, however.
const sections = document.querySelectorAll('section');
const out = document.getElementById('out');
const settings = {
pagination: true
}
sections.forEach((section, i) => {
const index = i + 1;
const id = section.id;
section.classList.add('ops-section');
section.dataset.index = index;
if (settings.pagination) {
const para = `<p class="lead">${id}</p>`;
const li = `<li><a data-index="${index}" href="#${index}" class="active">test</a>${para}</li>`;
out.insertAdjacentHTML('beforeend', li);
}
});
<section class="page1" id="name1">section1</section>
<section class="page2" id="name2">section2</section>
<section class="page3" id="name3">section3</section>
<ul id="out"></ul>
Why not using JQuery ? This is a small demo on how you can get the id attribute of your section and use it in the JQuery code:
$(document).ready(function() {
$('section').each(function( key, value ) {
// alert($(this).attr('id') + " - " + key + ": " + value );
$('pagination').append("<p class='lead'>* <a data-index='"+ key +"' href=#></a>" + $(this).attr('id') + '</p>');
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="page1" id="name1"></section>
<section class="page2" id="name2"></section>
<section class="page3" id="name3"></section>
<pagination></pagination>
I am trying to set value to my input but I want to do this while my data ends. As you know id must be unique in html. So i created an 'element' variable and I am increasing it.
I want to make my input text my model's CustomerName.
That's my code.
var element = 0;
var HTML = "";
while (element !== 5) {
HTML += "<input id=senderName" + element + " class=senderNameText type=text>";
document.getElementById("senderName" + element).value = "#Html.DisplayFor(model => model.CustomerName)";
element++;
}
div.innerHTML = HTML;
document.body.appendChild(div);
When I check it at console in Chrome it is written as "Cannot set property 'value' of null".
What should I do.
Thanks.
Your code should be like the following (note the quotes ' in creation of he new inputs are necessary) :
var element = 0;
var HTML = "";
while (element !== 5) {
div.innerHTML = "<input id='senderName" + element + "' class='senderNameText' type='text'>";
document.body.appendChild(div);
document.getElementById("senderName" + element).value = "#Html.DisplayFor(model => model.CustomerName)";
element++;
}
Now the problem come when you try to select new elements from the document and you're not yet append them so you get the message error that mean JS can't find any elements by the ids you're given.
So you should append the new HTML to the DOM inside loop.
Hope this helps.
I have a string with multiple elements with id's like below:
var data = "<div id='1'></div><input type='text' id='2'/>";
Now I'm using this regex to find all the id's in the string:
var reg = /id="([^"]+)"/g;
Afterwards I want to replace all those id's with a new id. Something like this:
data = data.replace(reg, + 'id="' + reg2 + '_' + numCompare + '"');
I want reg2, as seen above, to return the value of the id's.
I'm not too familiar with Regular Expressions, so how can I go about doing this?
Instead of using regex, parse it and loop through elements. Try:
var data = "<div id='1'></div><div id='asdf'><input type='text' id='2'/></div>",
numCompare = 23,
div = document.createElement("div"),
i, cur;
div.innerHTML = data;
function updateId(parent) {
var children = parent.children;
for (i = 0; i < children.length; i++) {
cur = children[i];
if (cur.nodeType === 1 && cur.id) {
cur.id = cur.id + "_" + numCompare;
}
updateId(cur);
}
}
updateId(div);
DEMO: http://jsfiddle.net/RbuaG/3/
This checks to see if the id is set in the first place, and only then will it modify it.
Also, it is safe in case the HTML contains a comment node (where IE 6-8 does include comment nodes in .children).
Also, it walks through all children of all elements. In your example, you only had one level of elements (no nested). But in my fiddle, I nest the <input /> and it is still modified.
To get the get the updated HTML, use div.innerHTML.
With jQuery, you can try:
var data = "<div id='1'></div><div id='asdf'><input type='text' id='2'/></div>",
numCompare = 23,
div = $("<div>"),
i, cur;
div.append(data);
div.find("[id]").each(function () {
$(this).attr("id", function (index, attr) {
return attr + "_" + numCompare;
});
});
DEMO: http://jsfiddle.net/tXFwh/5/
While it's valid to have the id start with and/or be a number, you should change the id of the elements to be a normal identifier.
References:
.children: https://developer.mozilla.org/en-US/docs/DOM/Element.children
.nodeType: https://developer.mozilla.org/en-US/docs/DOM/Node.nodeType
jQuery.find(): http://api.jquery.com/find/
jQuery.attr(): http://api.jquery.com/attr/
jQuery.each(): http://api.jquery.com/each/
Try using
.replace(/id='(.*?)'/g, 'id="$1_' + numCompare + '"');
Regex probably isn't the right way to do this, here is an example that uses jQuery:
var htmlstring = "<div id='1'></div><input type='text' id='2'/>";
var $dom = $('<div>').html(htmlstring);
$('[id]', $dom).each(function() {
$(this).attr('id', $(this).attr('id') + '_' + numCompare);
});
htmlstring = $dom.html();
Example: http://jsfiddle.net/fYb3U/
Using jQuery (further to your commments).
var data = "<div id='1'></div><input type='text' id='2'/>";
var output = $("<div></div>").html(data); // Convert string to jQuery object
output.find("[id]").each(function() { // Select all elements with an ID
var target = $(this);
var id = target.attr("id"); // Get the ID
target.attr("id", id + "_" + numCompare); // Set the id
});
console.log(output.html());
This is much better than using regex on HTML (Using regular expressions to parse HTML: why not?), is faster (although can be further improved by having a more direct selector than $("[id]") such as giving the elements a class).
Fiddle: http://jsfiddle.net/georeith/E6Hn7/10/
I am trying to load pictures name from a xml object and append to div. I am getting confuse with append typing layout, not able to find where im doing typing mistake.
This is working
$("#nn").append("<img id='theImg' src='/pic/jas/pic1.jpg'/>");
This not working
$("#nn").append("<img id='theImg' src='/pic/jas/'" + customer.find("pic_name") + "/>");
My jquery script part is
function OnSuccess(response) {
var xmlDoc = $.parseXML(response.d);
var xml = $(xmlDoc);
pageCount = parseInt(xml.find("PageCount").eq(0).find("PageCount").text());
var pic_infoVar = xml.find("pic_info");
pic_infoVar.each(function () {
var customer = $(this);
$("#picDiv").append("<img id='theImg' src='/pic/jas/'" + customer.find("pic_name") + "/>");
});
$("#loader").hide();
}
Html Div tag
<div id="picDiv">
LoadPic
</div>
Provded that pic_name is infact an element in an XML data structure (ex: <pic_name>pic1.jpg</pic_name>), the code that will do what you want is:
$("#nn").append("<img id='theImg' src='/pic/jas/" + customer.find("pic_name").text() + "'/>");
This is how i used to do
document.getElementById('nn').innerHTML +='<img src="'+customer.find(\"pic_name\")+'"/>';
I have a function like this in JQuery and JS. I have a list of divs with checkboxes and am adding them to my list. This works fine for like 40 divs, but sometimes I have 2,000 and it crashes Chrome and crawls on FF. Anyway to make this faster?
function AddToList()
{
$('div[name="notadded"] input:checked').each(function(index)
{
var html = $(this).parents('div[name="notadded"]').html();
//get rid of the class that is used to gather checkboxes in select/deselect
html = html.replace('listvars', 'addedvars');
var var_id = $(this).attr('value');
var new_html = '<div id="added_' + var_id + '" name="added">' + html + '</div>';
//hide the one we are adding and remove the check
$(this).parents('div[name="notadded"]').hide();
$('div[name="notadded"] input[value="' + var_id + '"]').attr('checked', false);
//add the vars to the added list
$('.addedList').append(new_html);
step3 = '3b';
});
}
You're doing 2000 DOM manipulations, not a good way to go. Trying doing 2,000 string manipulations and one DOM insert.
function AddToList()
{
var new_html = "";
$('div[name="notadded"] input:checked').each(function(index)
{
var html = $(this).parents('div[name="notadded"]').html();
//get rid of the class that is used to gather checkboxes in select/deselect
html = html.replace('listvars', 'addedvars');
var var_id = $(this).attr('value');
var new_html += '<div id="added_' + var_id + '" name="added">' + html + '</div>';
//hide the one we are adding and remove the check
$(this).parents('div[name="notadded"]').hide();
$('div[name="notadded"] input[value="' + var_id + '"]').attr('checked', false);
//add the vars to the added list
step3 = '3b';
});
$('.addedList').append(new_html);
}
Also, from experience, unchecking 2,000 checkboxes is seriously performance intensive. I'll wager taking this line out:
$('div[name="notadded"] input[value="' + var_id + '"]').attr('checked', false);
Will change everything. I'd recommend rewriting this function as a string replace, it'll be a hell of a lot faster that way.