I have two DIVs, #placeholder AND #imageLoad. When the user clicks on a particular thumb its larger version (thumb2) should then appear in #imageLoad DIV.
Here is the jQuery that needs to be fixed:
$.getJSON('jsonFile.json', function(data) {
var output="<ul>";
for (var i in data.items) {
output+="<li><img src=images/items/" + data.items[i].thumb + ".jpg></li>";
}
output+="</ul>";
document.getElementById("placeholder").innerHTML=output;
});
//This is wrong!! Not working..
$('li').on({
mouseenter: function() {
document.getElementById("imageLoad").innerHTML="<img src=images/items/" +
data.items[i].thumb2 + ".jpg>";
}
});
Here is the external JSON file below (jsonFile.json):
{"items":[
{
"id":"1",
"thumb":"01_sm",
"thumb2":"01_md"
},
{
"id":"2",
"thumb":"02_sm",
"thumb2":"02_md"
}
]}
$.getJSON('jsonFile.json', function(data) {
var output="<ul>";
for (var i = 0; i < data.items.length; i++) {
output += "<li><img thumb2='" + data.items[i].thumb2 + "' src='images/items/" + data.items[i].thumb + ".jpg'></li>";
}
output += "</ul>";
$("#placeholder").html(output);
$('li').on({
mouseenter: function() {
$("#imageLoad").html("<img src='images/items/" + $(this).find('img').attr('thumb2') + ".jpg'>");
}
});
});
Your variable data is declared only within the callback function of your getJSON call and hence it is not available in the other methods/ event handlers. Store it to a global variable when you get it. Like Below:
var globalData;
$.getJSON('jsonFile.json', function(data) {
globalData = data;
var output="<ul>";
for (var i in data.items) {
output+="<li id=\"listItem" + i + "\"><img src=images/items/" + data.items[i].thumb + ".jpg></li>";
}
output+="</ul>";
document.getElementById("placeholder").innerHTML=output;
});
//This is wrong!! Not working..
$('li').on({
mouseenter: function() {
var index = parseInt($(this).attr('id').substring(8));
document.getElementById("imageLoad").innerHTML="<img src=images/items/" +
globalData.items[index].thumb2 + ".jpg>";
}
});
Firstly, $.getJSON is asynchronous, so the binding of mouseenter following the async function will not work as the li elements does not exist when you attach the event handler. Secondly, store the second image source in a data attribute on each li element, and just retrieve that data attribute in the mouseenter function:
$.getJSON('jsonFile.json', function(data) {
var out = $("<ul />");
for (var i in data.items) {
$('<li />', {
src: 'images/items/' + data.items[i].thumb + '.jpg'
}).data('big', data.items[i].thumb2).appendTo(out);
}
$("#placeholder").html(out);
});
$('#placeholder').on('mouseenter', 'li', function() {
var bigImg = $('<img />', {
src: 'images/items/' + $(this).data('big') + '.jpg'
});
$("#imageLoad").html(bigImg);
});
Related
I have a function that Creates new items and allows you to Delete, Update and Save the inputs on these items using localStorage
However, if I have more than one item and then update and save the changes, those changes are applied over all items.
The problem is encountered at the $(".save").click(function() but I'm not sure I have set up my .items with a proper array.
Since I use localStorage the working code can be found in the pen below:
https://codepen.io/moofawsaw/pen/NoBQKV
window.localStorage.clear();
//create localStorage item
if (!localStorage.getItem("_storage")) {
localStorage.setItem("_storage", "");
}
//set data to localStorage function
function saveData() {
localStorage.setItem("_storage", $("#content").html());
}
// Open the create dialgoue:
$(".add").on("click", function() {
$(".create").toggle();
});
//Save the entered inputs and post the item:
$(".post").click(function() {
var id = $(".createtext").val();
var createtitle = $(".createtitle").val();
var item = "";
if (id[0]) {
for (var i = 0; i < id.length; i++) {
item += "<div>" + id[i] + "</div>";
}
} else {
item = "<div>Click update to add a card</div>";
}
$("#content").append(
'<div class="item">' +
'<div class="title">' +
createtitle +
"</div>" +
"<div class='text'>" +
id +
"</div>" +
'<button class="delete">Delete</button>' +
'<button class="update">Update</button>' +
"</div>"
);
$(".createtitle").val("");
$(".createtext").val("");
$(".create").toggle();
saveData();
});
//Close out of creating a new item
$(".close").click(function() {
$(".createtitle").val("");
$(".createtext").val("");
$(".create").toggle();
});
//Get inputs and open edit window to update the items:
$("#content").on("click", ".update", function() {
var item = $(this).closest(".item");
$(".updatetext").val(
$(this)
.closest(".item")
.find(".text")
.text()
);
$(".updatetitle").val(
$(this)
.closest(".item")
.find(".title")
.text()
);
$(".edit").toggle();
});
//Save changes and update the items (error:changes all items when clicked):
$(".save").click(function() {
var id = $(".updatetext").val();
var title = $(".updatetitle").val();
var item = "";
if (id[0]) {
for (var i = 0; i < id.length; i++) {
item += "<div>" + id[i] + "</div>";
}
} else {
item = "<p>Click edit to add a card</p>";
}
$(".item").each(function() {
$(this).html(
'<div class="title">' +
title +
"</div>" +
"<div class='text'>" +
id +
"</div>" +
'<button class="delete">Deleted(2)</button>' +
'<button class="update">Updated(2)</button>'
);
});
$(".updatetext").val("");
$(".updatetitle").val("");
$(".edit").toggle();
saveData();
});
//Discard any of these changes:
$(".discard").click(function() {
$(".updatetext").val("");
$(".updatetitle").val("");
$(".edit").toggle();
});
//Delete an item:
$("#content").on("click", ".delete", function() {
$(this)
.closest(".item")
.remove();
saveData();
});
$(function() {
if (localStorage.getItem("_storage")) {
$("#content").html(localStorage.getItem("_storage"));
}
});
Point is, you call .each() in your update callback.
$(".item").each(function() {
$(this).html(
'<div class="title"> ....'
);
});
This literally means "Find all DOM elements with item class and replace their contents with given html.
But you need to replace contents of the one specific element, on which Update button was clicked. To do so, you need to persist that element somehow.
One of the ways to do that with minimum changes to your code - introduce a variable in a scope available for both update and save functions. But in your case it would be a global variable, and those are not generally a good idea.
So I'd suggest to wrap all your code into a function (like $(function() {});.
Then you can introduce a local variable:
$(function () {
// define it
var $selectedItem;
// assign a value in the update click callback
$('#content').on('click', '.update', function () {
$selectedItem = $(this).closest('.item');
// ...
});
// read the value in the save click callback
$('.save').click(function () {
// ...
$selectedItem.html('...');
// ...
});
});
Example: https://codepen.io/anon/pen/GzXaoV
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 4 years ago.
I am using jquery to pull in JSON and loop through the results to create a collection of items. Namely, Anchors with a few data attributes. I then am watching those items so that on click I can pull the relevant data attributes and... do things with that data. When I hard code in the anchors with the data, everything works great. When I create them dynamically through the JSON I get nothing, and the page refreshes.
jQuery( document ).ready(function() {
$.getJSON("http://glacier.creativefilterdev.com/wp-json/wp/v2/chocolate?categories=42&per_page=100&order=asc", function(result) {
$.each(result, function(i, item) {
if(item.better_featured_image == null){
} else {
$(".white").append("<a class=\"choc-option\" href=\"\" data-text=\"" + item.title.rendered + ", \" data-img=\"" + item.better_featured_image.media_details.sizes.thumbnail.source_url + "\"><img class=\"chocolate\" src=\"" + item.better_featured_image.media_details.sizes.thumbnail.source_url + "\"><br>" + item.title.rendered + "</a>");
}
});
});
jQuery("a[data-text]").click(function(){
if(howMany < countVar) {
howMany += 1;
var imgurl = jQuery(".single").val();
var structure = jQuery('<div class="single" data-text="'+ jQuery(this).attr('data-text') +'"><img src="'+ jQuery(this).attr('data-img') +'"><a class="delete">-</a></div>');
jQuery('.buildbox').append(structure);
jQuery("#alert").css('display', 'none');
} else {
jQuery("#alert").css('display', 'block');
}
return false;
});
});
I apologize as there is code in there that I haven't explained as that is the "stuff" I'm doing with the data from the anchors.
You can do it like this
jQuery( document ).ready(function() {
$.getJSON("http://glacier.creativefilterdev.com/wp-json/wp/v2/chocolate?categories=42&per_page=100&order=asc", function(result) {
$.each(result, function(i, item) {
if(item.better_featured_image == null){
} else {
$(".white").append("<a class=\"choc-option\" href='' data-text=\"" + item.title.rendered + ", \" data-img=\"" + item.better_featured_image.media_details.sizes.thumbnail.source_url + "\"><img class=\"chocolate\" src=\"" + item.better_featured_image.media_details.sizes.thumbnail.source_url + "\"><br>" + item.title.rendered + "</a>");
}
});
});
$('body').on('click', 'a[data-text]', function(e) {
e.preventDefault();
alert('test');
if(howMany < countVar) {
howMany += 1;
var imgurl = jQuery(".single").val();
var structure = jQuery('<div class="single" data-text="'+ jQuery(this).attr('data-text') +'"><img src="'+ jQuery(this).attr('data-img') +'"><a class="delete">-</a></div>');
jQuery('.buildbox').append(structure);
jQuery("#alert").css('display', 'none');
} else {
jQuery("#alert").css('display', 'block');
}
return false;
});
});
function demo(){
$('.box').slideToggle('fast');
}
$(document).ready(function(){
$.getJSON( "js/JobOpenings.json", function( data ) {
var glrScrlImg = [];
$.each( data.getJobOpeningsResult, function( key, val ) {
var st = "",id,st2= "",st3="",id;
st +="<h4>" + val.JobTitle + "</h4>";
st3 += "<div class='box'>" + val.JobDetails + "</div>";
$("#newsDetails").append("<li onclick='demo()'>" + st+val.JobSector + "<br>" + st3 + "</li>");
$('.box').hide();
});
});
});
I am reading data from a json file. The div with 'box' class is hidden. Currently this code is displaying all div on click of the li. What changes should I make to display only the div corresponding to the clicked li?
Here what we need to do is to find the .box element within the clicked li, so we need to get a reference to the clicked element.
I would use a delegated jQuery event handler with css to initially hide the element
$(document).ready(function () {
$('#newsDetails').on('click', 'li', function () {
$(this).find('.box').toggleClass('hidden');
})
$.getJSON("js/JobOpenings.json", function (data) {
var glrScrlImg = [];
$.each(data.getJobOpeningsResult, function (key, val) {
var st = "",
id, st2 = "",
st3 = "",
id;
st += "<h4>" + val.JobTitle + "</h4>";
st3 += "<div class='box hidden'>" + val.JobDetails + "</div>";
$("#newsDetails").append("<li>" + st + val.JobSector + "<br>" + st3 + "</li>");
});
});
});
with css
.hidden {
display: none;
}
Pass the control to the function and then based on your control slideToggle its respective .box
function demo(ctrl){
$(ctrl).find('.box').slideToggle('fast');
}
$(document).ready(function(){
$.getJSON( "js/JobOpenings.json", function( data ) {
var glrScrlImg = [];
$.each( data.getJobOpeningsResult, function( key, val ) {
var st = "",id,st2= "",st3="",id;
st +="<h4>" + val.JobTitle + "</h4>";
st3 += "<div class='box'>" + val.JobDetails + "</div>";
$("#newsDetails").append("<li onclick='demo(this)'>" + st+val.JobSector + "<br>" + st3 + "</li>");
$('.box').hide();
});
});
});
Or add a class to li and attach an event handler like below instead of writing inline onclick as below:
$("#newsDetails").append("<li class="someclass"'>" + st+val.JobSector + "<br>" + st3 + "</li>");
and then instead of function demo() write this
$('#newsDetails').on('click','.someclass',function(){
$(this).find('.box').slideToggle('fast');
});
UPDATE
Method 1:
function demo(ctrl){
$('#newsDetails').find('li.box').hide('fast'); //hide all the .box
$(ctrl).find('.box').slideToggle('fast');
}
Method 2:
$('#newsDetails').on('click','.someclass',function(){
$('#newsDetails').find('li.box').hide('fast'); //hide all the .box
$(this).find('.box').slideToggle('fast');
});
UPDATE 2:
Method 1:
function demo(ctrl){
$('#newsDetails').find('li.box').not($(ctrl).find('.box')).hide('fast'); //hide all the .box
$(ctrl).find('.box').slideToggle('fast');
}
Method 2:
$('#newsDetails').on('click','.someclass',function(){
$('#newsDetails').find('li.box').not($(ctrl).find('.box')).hide('fast'); //hide all the .box except this
$(this).find('.box').slideToggle('fast');
});
You should structure your html (which is missing from the question!) so that the div and li are "connected" in some way (maybe the div is child of li, or they have same class, ecc).
Right now the line
$('.box').slideToggle('fast');
is applied to all element with class '.box' in your page. You want to be more selective there, that's where the way you structure the html comes into play.
Here's an example: http://jsfiddle.net/owe0faLs/1/
In my MVC project, I have passed a list of users to my view, and inside this view I iterate through the list and create an anchor tag for each user. Would it be possible to add a delay after each anchor tag is created? Here is my jquery:
DeskFunction.prototype.init = function() {
var self = this;
var element;
this.allData = #Html.Raw(#JsonConvert.SerializeObject(Model.AllDeskData));
for (var i = 0; i < this.allData.length; i++) {
element = $("<a href='#' class='deskBtn tooltip fancybox' title='" + this.allData[i].Name + "' data-name='" + this.allData[i].UserName + "' data-department='" + this.allData[i].DepartmentName + "'></a>");
$(element).css({
"top": this.allData[i].DeskYCoord,
"left": this.allData[i].DeskXCoord
}).appendTo(".map").show('normal').delay(3000);
$(element).on('click', function() {
var user = $(this).attr("data-name");
$.ajax({
url: "/Home/GetUserData",
type: "GET",
data: { user: user },
success: function(data) {
$(".user-data .name").text(data.displayName);
}
});
});
}
$('.tooltip').tooltipster();
$('.search-user').keyup(function() {
self.search();
});
};
I would like the first tag to be created and added to the map, then a delay of a second, after that the next anchor tag would be added, is this possible? Any help would be appreciated
you can try below code. inside setTimeout write all code that you want to do under the loop. It will get called after 1 sec
for (var i = 0; i < this.allData.length; i++) {
(function(i, self ){
setTimeout(function(){
// ALL LOOP CODE HERE
// use self.allData
}, 1000);
}(i, self ));
}
You could place your code in a setTimeout.
DeskFunction.prototype.init = function() {
var self = this;
var element;
this.allData = #Html.Raw(#JsonConvert.SerializeObject(Model.AllDeskData));
for (var i = 0; i < this.allData.length; i++) {
element = $("<a href='#' class='deskBtn tooltip fancybox' title='" + this.allData[i].Name + "' data-name='" + this.allData[i].UserName + "' data-department='" + this.allData[i].DepartmentName + "'></a>");
$(element).css({
"top": this.allData[i].DeskYCoord,
"left": this.allData[i].DeskXCoord
}).appendTo(".map").show('normal');
setTimeout(function(){
$(element).on('click', function() {
var user = $(self).attr("data-name");
$.ajax({
url: "/Home/GetUserData",
type: "GET",
data: { user: user },
success: function(data) {
$(".user-data .name").text(data.displayName);
}
});
});
}, 3000);
}
$('.tooltip').tooltipster();
$('.search-user').keyup(function() {
self.search();
});
};
With timeouts incrementing and passing all data through a function call to a local scope the following could should do:
for(var i in this.allData) {
// create local function scope saving i and data from being altered by next iterations
(function(i, data) {
setTimeout(function() {
// place your code here, using data (and i)
}, i*1000);
})(i, this.allData[i]);
}
For each element of this.allData this.allData[i] is passed to the local function as data, along with each i. These are used in the inner scope for setting timeouts in intervalls of 1 sec and creating the anchor links.
You actually want an asynchronous loop:
DeskFunction.prototype.init = function() {
this.allData = #Html.Raw(#JsonConvert.SerializeObject(Model.AllDeskData));
var self = this,
allData = this.allData,
delay = 1000;
var appendDeleteLink = function appendDeleteLink(i) {
if (i >= allData.length) {
return;
}
var element = $("<a href='#' class='deskBtn tooltip fancybox' title='" + allData[i].Name + "' data-name='" + allData[i].UserName + "' data-department='" + allData[i].DepartmentName + "'></a>");
$(element).css({
"top": allData[i].DeskYCoord,
"left": allData[i].DeskXCoord
}).appendTo(".map").show('normal').delay(3000);
$(element).on('click', function() {
var user = $(this).attr("data-name");
$.ajax({
url: "/Home/GetUserData",
type: "GET",
data: { user: user },
success: function(data) {
$(".user-data .name").text(data.displayName);
}
});
});
(function(a) {
setTimeout(function() {
appendDeleteLink(a);
}, delay);
})(++i);
};
appendDeleteLink(0);
$('.tooltip').tooltipster();
$('.search-user').keyup(function() {
self.search();
});
};
I have an JSON array, that i manipulate inside my app,
...
$.each(data, function(key, val) {
val = link + val;
foto = val;
foto = foto.substr(0, foto.lastIndexOf(".")) + ".jpg";
/* Visualizza */
var elem = document.getElementById("archivio-num");
elem.innerHTML = '<img src="' + foto + '">';
elem.firstChild.onclick = function() {
cordova.exec("ChildBrowserCommand.showWebPage", val);
};
items.push('<li id="' + key + '">' + elem.innerHTML + '</li>');
});
...
Now i'm trying to push all elements outside that are packed inside var elem.
Puttin only + elem + give me an error [objectHTMLDivElement].
Is that possible?
Exploiting jQuery further, you might want to try something like this :
...
var $ul = $("<ul/>");//jQuery object containing a dummy UL element in which to accumulate LI elements.
$.each(data, function(key, val) {
var url = link + val;
var foto = url.substr(0, url.lastIndexOf(".")) + ".jpg";
var $a = $('<a/>').attr('href',url).append($("<img/>").attr('src',foto)).on('click', function() {
cordova.exec("ChildBrowserCommand.showWebPage", $(this).attr('href'));
return false;
});
$ul.append($('<li/>').attr('id',key).append($a));
});
$("#archivio-num").html($a);
...
Here, instead of accumulating the HTML in an array, actual LI elements are accumulated in a jQuery-wrapped UL element, which is available for further treatment (eg. insertion into the DOM) later in the code.
<script type="text/javascript">
$.getJSON('http://www..../json.php', function(data) {
var items = [];
var url;
var foto;
var link = 'http://www.bla.com/';
var $div = $("<div/>");
$.each(data, function(key, val) {
url = link + val;
foto = url.substr(0, url.lastIndexOf(".")) + ".jpg";
var $a = $('<a>').attr('href',url).append($("<img/>").attr('src',foto)).on('click',function(){
cordova.exec("ChildBrowserCommand.showWebPage", $(this).attr('href'));
return false;
});
$div.append($('<div/>').attr('id',key).append($a));
});
$("#archivio-num").html($a);
});
</script>