jQuery.data only saves data from the last Element - javascript

i am trying to use jQuery.data() and save an Object to my HTML-Elements. Everytime i add an list-Element to my unordered List it only saves the last object to the specific li-Element. Every other li-Elements saved data gets thrown away!
I've built a little Example. JSBin-Example
On the left, i create a List with an Object saved to it. On the right i am trying to show the data related to the Object.
Why does it only show the Object related to the last HTML-Element?
Working example:
JSBin-Example

That's because you are modifying innerHTML property of the wrapper element. What happens is in each iteration the elements are regenerated, the current elements are removed and the new elements don't have any stored data. Using innerHTML property is the worst way of modifying element contents. You just need to create a li element and append it to the wrapper element:
var random = 0;
// var testObject = [];
function addNewItem(){
random += 1;
var id = "testId" + random;
var text = "This is my " + random + ". text";
var data = {id: id, text: text};
// testObject.push(data);
// You can pass an object as the second argument
// to jQuery constructor and it calls the
// corresponding methods as setter
$('<li></li>', {
text: text + JSON.stringify(data),
id: id,
data: data
}).appendTo('#listId');
}
// bind and trigger click event
$("#add").on('click', addNewItem).click();

I changed
for(var i = 0; i < testObject.length; i++){
var listItem = "";
var id = testObject[i].id;
listItem += liStart + id + liStart2;
listItem += testObject[i].text;
listItem += liEnd;
unorderedList.innerHTML += listItem;
$("#"+id).data(testObject[i]);
}
to this in your updatelist function
//for(var i = 0; i < testObject.length; i++){
var id = testObject[testObject.length-1].id;
listItems += liStart + id+"savedData" + liStart2;
listItems += JSON.stringify($("#"+id).data());
listItems += liEnd;
//}
savedData.innerHTML += listItems;
and it fixed the issue

To help you understand my comment on the question I thought it best I'd give an example of what I meant.
I didn't have enough time to fully go through the solution but wanted to give an example of what I'd call more readable code.
I've added all variables at the top of the function. This will allow you to read and find items much quicker if you needed to alter them.
I've also merged a lot of the string values that you had into an object, namely the li element.
I've never used $.data() as an object before so wasn't really aware how I could use it to set the values in the updateSavedData() $('li'), although the console.log() does show the correct key / values.
$(document).ready(function(){
var uID = 0;
var testObject = [];
var unorderedList = $("#listId");
var savedList = $("#savedData");
var TOL = 0; //TestObjectLength
var textTemplate = "This is my [0] text!";
function addNewItem(){
uID++;
testObject.push({id: uID, text: textTemplate.replace("[0]", uID)});
TOL = testObject.length-1;
updateList();
}
function updateList(){
var li = $('<li>', { id: testObject[TOL].id, data: testObject[TOL], text: testObject[TOL].text });
li.appendTo(unorderedList);
updateSavedData(li.data());
}
function updateSavedData(li){
console.log(JSON.stringify(li));
$('<li>', JSON.stringify(li)).appendTo(savedList);
}
addNewItem();
$("#add").on('click', addNewItem);
});
Working Example
http://jsbin.com/ralizazahe/1/edit?js,console,output
Anyone that wants to progress on that please do as I'd also like to see how this could be progressed more.
Update
Taken it a step more and refactored to this
$(document).ready(function(){
var $displayList = $("#listId");
var $savedList = $("#savedData");
var textTemplate = "This is my {0} text!";
var uID = 0; //Unique ID
var data = { id: null, text: null }; //Gives a reference
function init(){
uID++;
data = { id: uID, text: textTemplate.replace("{0}", uID) };
}
function addNewItem(){
init();
$('<li>', data).appendTo($displayList);
updateSavedData(data);
}
function updateSavedData(li){
$('<li>', li).appendTo($savedList);
}
addNewItem();
$("#add").on('click', addNewItem);
});
http://jsbin.com/bajekagoli/1/edit?js,console,output

Related

How can I save DOM elements inside of a javaScript object? I want to update the page by updating the object

How can I save DOM elements inside of a javaScript object? I want to update the page by updating the object.
Bellow I created and Added elements to the website using a for loop. I want to save some of those elements inside some objects properties so that I can update the html in the page by updating the textContent of the property value in the object.
for ( var i = 1 ; i <= numberOfPlayers ; i++ ){
//create object for each player.
var name = nameInputArray[i].value;
players["player" + i] = {
name: name,
score: 0,
fireStreak: 0,
xStreak: 0,
html: {}
}
//Create a Player box for each player.
var playerBox= document.createElement('div');
playerBox.classList.add('player_box');
document.getElementById('player-box-container').appendChild(playerBox);
var playerName = document.createElement('div');
playerName.textContent = players["player" + i].name;
playerName.classList.add('player_names');
playerBox.appendChild(playerName);
var playerScoreHeading = document.createElement('div');
playerScoreHeading.classList.add('player_score_heading');
playerScoreHeading.textContent = "Player Score:";
playerBox.appendChild(playerScoreHeading);
var playerScoreDiv = document.createElement('div');
playerScoreDiv.classList.add('player_score');
playerBox.appendChild(playerScoreDiv);
var playerScoreNumber = document.createElement('span');
playerScoreNumber.classList.add('player_score_number');
playerScoreNumber.textContent = players["player" + i].score;
playerScoreNumber.id = "player_score_number_" + i;
playerScoreDiv.appendChild(playerScoreNumber);
//THIS IS THE PART NOT WORKING!!!!!!!!!!!!!!!!!(THE NEXT LINE)
players["player" + i].html.score = document.getElementById("player_score_number_" + i);
playerScoreDiv.innerHTML += "pts";
}
for some reason when I try to access:
function changeScore(){
players.player1.html.score.textContent = 30;
}
It would change the inside the object but it will not render on the webpage.
Please help.
By reassigning an HTML string to playerScoreDiv.innerHTML, you lose all the element objects that you had previously created as descendants of playerScoreDiv: they are recreated (with obviously different object references) from the HTML.
So instead of:
playerScoreDiv.innerHTML += "pts";
do:
playerScoreDiv.appendChild(document.createTextNode('pts'));

Javascript For loop appending child only appends first element, then throws error

I'm looping through a js object with a nested for loop, stated below, it appends the first element correctly, but then throws the following error:
Can't set the property className of an undefined reference or empty reference. (not sure if exact error, translating from Dutch...)
function allVideos() {
var sql = "SELECT videos.VideoName, videos.VideoPath FROM videos";
var resultSet = db.query(sql, {json:true}); //returns: [{"VideoName":"timelapse aethon2","VideoPath":"videos\\Roermond Papier\\160424 Time laps Aethon2.avi"},{"VideoName":"timelapse aethon3","VideoPath":"videos\\Roermond Papier\\160424 Time laps Aethon2.avi"}]
var parsed = JSON.parse(resultSet);
var parsedlength = arrLenght(parsed);
//alert(resultSet);
for(var i = 0; i < parsedlength; i++) {
var obj = parsed[i];
//alert(i);
var videoElement = document.getElementById("allVideos");
for (var key in obj) {
if(obj.hasOwnProperty(key)) {
videoElement.appendChild(document.createElement('div'));
videoElement.children[i].id='allVid' + i;
videoElement.children[i].className='col-md-4 col-xs-12';
//alert(typeof key)
var card = document.getElementById('allVid' + i);
alert(i);
card.appendChild(document.createElement('div'));
card.children[i].className='card card-block';
card.children[i].innerHTML = "<h3 class='card-title'>" + obj['VideoName'] + "</h3><button class='btn btn-primary'>Selecteren</button>"
}
}
}
}
[EDIT] added screenshot of how it looks
Your code has some significant logic issues. You're using nested loops, but appending to an element assuming that the outer loop counter will let you index into that element's children to get the element you just appended. Later, you try to get that same element again using getElementById. Then, you append a new element to your newly-created element, but try to access that new element using children[i] on the one you just created — at that point, the card element will only have a single child, so as of the second outer loop, it will fail.
createElement returns the element to you, so there's no reason at all to try to access it via children[i] (either time) or getElementById.
See comments:
function allVideos() {
var sql = "SELECT videos.VideoName, videos.VideoPath FROM videos";
var resultSet = db.query(sql, {json:true});
var parsed = JSON.parse(resultSet);
var parsedlength = arrLenght(parsed);
for(var i = 0; i < parsedlength; i++) {
var obj = parsed[i];
//alert(i);
var videoElement = document.getElementById("allVideos");
for (var key in obj) {
if(obj.hasOwnProperty(key)) {
// Create the card, give it its id and class
var card = document.createElement('div');
card.id='allVid' + i;
card.className='col-md-4 col-xs-12';
// Create the div to put in the card, give it its class and content
var div = document.createElement('div');
card.appendChild(div);
div.className='card card-block';
div.innerHTML = "<h3 class='card-title'>" + obj['VideoName'] + "</h3><button class='btn btn-primary'>Selecteren</button>"
// Append the card
videoElement.appendChild(card);
}
}
}
}
Side note: arrLenght looks like a typo (it should be th, not ht), but moreover, there's no reason to use a function to get the length of an array; it's available via the array's length property: parsedLength = parsed.length.
Side note 2: You may find these ways of looping through arrays useful.
Your problem is the if within the nested for:
if(obj.hasOwnProperty(key)) { ...
The variable i is increased even if the property is not "owned" (when the if condition returns false), so next time that the condition is true, i is out of bounds.

Why can't I append to a <p> tag?

I am working on the scripting a ToDo list webapp, and I am trying to take the contents of 4 text boxes to create the content of the ToDo item.
Currently, when I try to connect the elements generated from the form, I get the error TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
at HTMLButtonElement.document.getElementsByClassName.onclick
I am currently using a function to create the element that I want to append to the body of the ToDo item, and I believe I am returning an element from my function. The code is posted below.
document.getElementsByClassName('modal-accept-button')[0].onclick = function () {
var formVals = {
what: document.getElementById('todo-input-what').value,
where: document.getElementById('todo-input-where').value,
when: document.getElementById('todo-input-when').value,
who: document.getElementById('todo-input-who').value,
details: document.getElementById('todo-input-details').value
};
document.getElementsByTagName('main')[0].appendChild(function () {
var fields = ["where", "when", "who", "details"];
var root = document.createElement("SECTION").className = "todo";
var title = document.createElement("H2").value = formVals.what;
var body = document.createElement("DIV").className = "todo-body"
for (var i = 0; i < fields.length; i++) {
var currentField = fields[i];
var currentVal = formVals.currentField;
body.appendChild(function () {
var p = document.createElement("P").className = "indent-wrapped";
var span = document.createElement("SPAN").class = currentField;
var text = document.createTextNode(currentVal);
span.value = currentField + ": ";
p.appendChild(span);
p.appendChild(text);
return p;
});
}
root.appendChild(title);
root.appendChild(body);
return root;
});
resetModal();
}
The variable p is not an item (HTML "Node"); it is a string.
That is, because you assigned it a string, using a sequence assignment (the last value goes all the way back) - "indent-wrapped" goes into className and then className goes into p.
Separate the item creation from the class assignment:
var p = document.createElement("P")
p.className = "indent-wrapped"
Same goes for root, title and span. They all are being assigned strings the same way.
You assign the string indent-wrapped to p, as you can see in the following snippet. So you try to call appendChild on a string instead of a node. Split the assignment, so you will first assign the node to p, and in the next statement set its classname. The same goes for the other elements (root, title, body, etc) where you try to create the element and set one of their properties in the same statement.
var p = document.createElement("P").className = "indent-wrapped";
console.log(p);

search for element within element by tagname

After being stuck for a few hours on this problem, i think it is time for call for help on this.
Situation
I have a XML file which i need to filter and group. I've managed to filter it with the :Contains part. I've also determined the nodes on which i need to group (the getGroups function gives those back to me). Now i want to create a new XML with the filtered values and grouped by the returned keys.
Code
var XMLElement = document.createElement("DataElementsCalc");
jQuery(xml).find("DataElements " + topNodes + filter).each( function() {
var dataSetTemp = this.parentNode;
if(calculation1 != "")
{
var groupKeys = getGroups(dataSetTemp,calculation1);
var tempXML = XMLElement;
jQuery(groupKeys).each(function (key,value) {
var tempValue = 'a' + value.toLowerCase().replace(/\W/g, '');
if(tempXML.getElementsByTagName(tempValue).length > 0)
{
tempXML = tempXML.getElementsByTagName(tempValue);
}
else
{
var Node = document.createElement(tempValue);
tempXML.appendChild(Node);
tempXML = Node;
}
});
var Node = document.createElement("InfoSet");
var x = dataSetTemp.childNodes;
for (i=0; i < x.length; i++)
{
if(x[i].nodeType == 1)
{
var tempElement = document.createElement(x[i].nodeName);
tempElement.innerHTML = x[i].childNodes[0].nodeValue;
Node.appendChild(tempElement);
}
}
tempXML.appendChild(Node);
}
});
Explanation
As said in the situation, i already filtered the XML and have the groupNames from the getGroups function. There are a few other things i need to explain for this code:
tempValue is being build as a a + value.toLowerCase().replace(/\W/g, '');. This is being done because i possible get dates into the groupKeys. This way the node name is getting a working name (i received errors on other ways).
I want to create a new XML which is leveled by the groups. If a group already exists, i want to create a new element in it, not get a new group with the same name. (my problem at the moment).
Problem
As mentioned above, the groups aren't checked properly. Firstly: tempXML.getElementsByTagName(tempValue).length returns the error that the function tempXML.getElementsByTagName does not exists. Secondly: If i change this to document.getElemetsByTagName I get a lot of the same nodes in my XML file.
Effect
The grouping effect doesn't work as it should. I get OR an error, OR a lot of the same nodes in my DataElementsCalc.
Questions
How can i solve this? How do create nodes beneath specific nodes (for if there is a group A beneath group 1 as well as beneath group 2)?
Tried
Change tempXML to document on different places (all getElementsByTagName, at the same time or not). Looked for another way to create a XML which is easier to handle (haven't found one, yet)
As mentioned by myself in the comments of the question:
I also don't see anything in the source code for this (maybe this is the reason why it doesn't work??)
I tried to place the XMLElement into an existing element on my webpage (like this:
var XMLElement = document.createElement("DataElementsCalc");
jQuery('.basicData').append(XMLElement);
in which basicData is a class of an existing element).
Now i do get a list of all elements ordered on the groups i wanted.
Final version
var XMLElement = jQuery("<DataElementsCalc/>");
jQuery('.basicData').append(XMLElement);
jQuery(xml).find("DataElements " + topNodes + filter).each( function()
{
aantalElementsTotal++;
var dataSetTemp = this.parentNode;
if(calculation1 != "")
{
var groupKeys = getGroups(dataSetTemp,calculation1);
var tempXML = XMLElement;
var groupId = '';
jQuery(groupKeys).each(function (key,value) {
var tempValue = 'a' + value.toLowerCase().replace(/\W/g, '');
groupId += 'a' + value.toLowerCase().replace(/\W/g, '');
if(jQuery("#" + groupId).length > 0)
{
tempXML = jQuery("#" + groupId);
}
else
{
var Node = jQuery("<"+tempValue+"/>");
jQuery(Node).attr('id', groupId);
jQuery(tempXML).append(Node);
tempXML = Node;
}
});
var Node = jQuery("<InfoSet/>");
var x = dataSetTemp.childNodes;
for (i=0; i < x.length; i++)
{
if(x[i].nodeType == 1)
{
var tempElement = jQuery("<"+x[i].nodeName+"/>");
jQuery(tempElement).text(x[i].childNodes[0].nodeValue);
jQuery(Node).append(tempElement);
}
}
jQuery(tempXML).append(Node);
}
});

Using data attributes in JavaScript

I am trying to use the data attribute to change the page to details about the selected product in my index web page.
These are the functions that I am using at the moment. This works but it will only display the innerHTML of the document. Whereas i want all of the data about the object that is stored in a data attribute called data-detail. The responseText would be something similar to this:
data-detail='{"ID":"1", "Name":"Some Name", "Description":"Some Description", "Price":"100", "Photo":"SomePath/AnotherPath/Image.png"}'
function displayItems(results){
article = document.getElementById("homeSection");
string = '<h1>Company Name</h1><h2>Why not try these products?</h2>';
for(var i=0; i<results.length; i++){
var price = parseFloat(results[i].Price);
var sec = document.createElement("section");
sec.classList.add("homeItem");
sec.dataset.detail = JSON.stringify(results[i]);
article.appendChild(sec);
sec.innerHTML = '<div class="imageContainer"><img class="resultsImage" src="' + results[i].Photo + '"></div><p class="resultsName">' + results[i].Name + '</p><p class="resultsPrice">£' + price.toFixed(2) + '</p>';
var items = document.querySelectorAll(".homeItem");
for(i=0; i<items.length; i++){
items[i].addEventListener("click", selectedProduct);
}
}
}
function selectedProduct(event){
target = event.currentTarget;
homeSection = document.getElementById("homeSection");
console.log(target.detail.ID);
homeSection.innerHTML = target.innerHTML;
}
After clicking on an element, the console is telling me that data.detail is undefined after parsing through the 3rd line down in the selectedProduct function. I wonder if anyone could tell me why this is and if I am being silly and not spotting the problem.
As you can store only string values, So after getting the value, you need to convert back to object by calling JSON.parse(). You also need to use dataset to get the value as you are setting the value.
function selectedProduct(event){
target = event.currentTarget;
homeSection = document.getElementById("homeSection");
console.log(JSON.parse(target.dataset.detail).ID);
homeSection.innerHTML = target.innerHTML;
}

Categories

Resources