Using data attributes in JavaScript - 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;
}

Related

Accessing variables from a parent function within a jQuery click() event (Word add-in)

The code for my add-in takes a search term, then displays a list of matching links on a table. Each link is supposed to insert itself into a word document when it's clicked, but I can't figure out how to pass variables in to a jQuery .click() function.
Currently, no matter what option I click, the URL of the link that gets inserted into the word document is always the URL of the last item on the list. So for example, if I had 3 results in the table: Facebook, Instagram and Yahoo, then whatever option I click, the URL that gets inserted is always http://www.yahoo.com
function displayLinks() {
// Empty pre-existing results
$('#linksTable').empty();
var filteredLinks = [];
// Grab value from search box
var searchTerm = document.getElementById('linksSearchField').value;
// Separate all links containing search term and put them in a filtered list
for (var i = 0; i < numberOfLinks; i++) {
if (sortedLinks[i].linkName.toLowerCase().includes(searchTerm.toLowerCase())){
filteredLinks.push(sortedLinks[i]);
}
}
// Get length of filtered links array
var numberOfSearchResults = filteredLinks.length;
// Populate table with loop
if (searchTerm != '') {
for (var x = 0; x < numberOfSearchResults; x++) {
var table = document.getElementById('linksTable');
var row = table.insertRow(x);
var nameCell = row.insertCell(0);
var linkName = filteredLinks[x].linkName;
var linkNameFormattedForID = linkName.replace(/([ &/!*^%$##+_-])+/g);
var linkURL = filteredLinks[x].linkURL;
// Add link to table
nameCell.innerHTML = "<a href='javascript:void(0);' id='" + linkNameFormattedForID + "'>" + linkName + "</a>";
// Code to add link to word document
$('#' + linkNameFormattedForID).click(linkName, function (linkName) {
Word.run(function (context) {
const doc = context.document;
const originalRange = doc.getSelection();
originalRange.insertHtml("<a href='" + linkURL + "'>" + linkName.currentTarget.innerText + "</a>", "Start");
originalRange.insertText("Refer to ", "Start");
originalRange.load("text");
return context.sync()
})
.catch(function (error) {
console.log("Error: " + error);
if (error instanceof OfficeExtension.Error) {
console.log("Debug info: " + JSON.stringify(error.debugInfo));
}
});
});
}
}
}
I think I could maybe fix the problem by defining the linkURL variable within the click function itself, but the issue is that I can't access filteredLinks[x] inside of it. I can access the filteredLinks array on its own, but it can't read x, even though the click function is contained within the loop?
As a last-resort super hacky fix, I think I could just change the ID of each item to include it's URL, then extract it from linkName.currentTarget.innerText, but I'd rather not do that unless I really have to.
Thanks in advance!
The problem is that var in JavaScript is function scoped, and because the click event gets invoked after the for loop ends, the value of x will always be the final value of x. There's more information in pleanty of blog posts like this one: https://medium.com/front-end-developers/es6-variable-scopes-in-loops-with-closure-9cde7a198744
The solution:
Use let or const instead of var
A slightly worse solution:
wrap the contents of the for loop inside an IIFE
for (var x = 0; x < numberOfSearchResults; x++) {
(function(x){
...
})(x);
}

Do I have to append the scraped data before I can extract the data?

I'm scraping data from another website. So far I have been successfull but I am struggling with one issue. When I've fetched the data I have to append it inside a div in order to extract the correct data using getElementByClassName. Is there any other way I can do this? Can I store the data somehow in a local variable and then extract the data in a similar way?
$.getJSON('URL' + encodeURIComponent('websiteURL' + retrievedData.postnr) + '&callback=?', function(data) {
$(".newHtml").append(data.contents);//Here is where I append the data so I can extract it.
var elements = document.getElementsByClassName("The class name that I am looking for");
for (var i = 1; i < elements.length; i++) {
var header = elements[i].getElementsByTagName("h2")[0].textContent;
$(".add").append(header);
for (var x = 0; x < 3; x++) {
var pris = elements[i].getElementsByClassName("Class name inside with certain content inside the other class")[x].textContent;
var info = elements[i].getElementsByClassName("Class name inside with certain content inside the other class")[x].textContent;
$(".add").append("<div class='priser'><h3 class='pris'>" + pris + "</h3>" + "<p class='info'>" + info + "</p></div>");
}
}
$(".newHtml").remove();
No need to append it to document. You can create jQuery object on the fly, then it can be used extract data using various DOM traversal methods.
var myObj = $('<div />').html(data.contents);
var tragetElement = myObj.find('.someClass')
var myObj = $('<div />').html('<span class="someClass">text</span>');
var tragetElement = myObj.find('.someClass');
console.log(tragetElement.text());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

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.

How to add/append to existing Web storage in javascript?

I am trying to add/append to new/existing localStorage using this code:
function add(){
var name = document.getElementById("name").value;
var ort = document.getElementById("ort").value;
var user = {"name":name, "ort":ort};
var exist = [];
var tmp = (JSON.parse(localStorage.getItem("users")));
if(tmp)
exist.push(tmp);
exist.push(user);
localStorage.setItem("users",JSON.stringify(exist));
console.log(JSON.parse(localStorage.getItem("users")));
var obj = JSON.parse(localStorage.getItem("users"));
for(var i=0; i<obj.length; i++) {
console.log('key: ' + i + '\n' + 'value: ' + obj[i]);
}
}
Looking into Chrome developer tools localStorage looks like this:
[[{"name":"a","city":"a"}],{"name":"b","city":"b"}]
But what I want is:
[{"name":"a","city":"a"},{"name":"b","city":"b"}]
What am I doing wrong?
Any help appreciated,
Best regards,
localStorage only store strings. You need to stringify your exist and then save it to storage
localStorage.setItem("users",JSON.stringify(exist));
To retrieve it use JSON.parse
var exist = JSON.parse(localStorage.exist); //Even for users object do the same
Update
To only push the objects inside the array tmp to exist use map():
tmp.map(function(item){
exist.push(item);
})

jQuery.data only saves data from the last Element

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

Categories

Resources