jQuery: click() triggers twice with dynamic content - javascript

I'm basically loading contents from a database over ajax into a div. Then, clicking on of these content-pieces should reload new content.
The ajax request is initialized as a method in a class which I call at the beginning:
function Request(tag1, tag2, tag3) {
this.tag1 = tag1,
this.tag2 = tag2,
this.tag3 = tag3
}
Request.prototype = {
ajaxCategoryRequest: function () {
$.ajax({
type: "GET",
url: "request2.php",
data: "tag1=" + this.tag1 + "&tag2=" + this.tag2 + "&tag3=" + this.tag3,
success: function (data) {
var jdata = $.parseJSON(data);
//Frage ausgeben
$("h1.question").html(jdata[0]);
//Tiles ausgeben
for (var i = 1; i < jdata.length; i++) {
$("#tiles").append(
'<div class="category" id="' + jdata[i].type + '" data-owntag="' + jdata[i].owntag + '" data-description="' + jdata[i].description + '"><img src="' + jdata[i].imageurl + '" alt="' + jdata[i].name + '"/><div class="ctitle">' + jdata[i].name + '</div></div>'
);
}
}
});
}
};
var searchtype = new Request("", "", "");
searchtype.ajaxCategoryRequest();
Then clicking one of the above created div's should start a new request:
$("#tiles").on('click', '.category', function () {
var tag1 = "newtag";
var tag2 = "newtag";
var tag3 = "newtag";
//remove old content
$('.category').remove();
//start new request
var nextrequest = new Request(tag1, tag2, tag3);
nextrequest.ajaxCategoryRequest();
});
});
Basically everything's working, the content is loaded, and if I click on a div, it does trigger a new request, but here comes the error, it's triggered twice. So, every new loaded element appears twice.
I searched for it, and I think it's due to the printing-loop in the ajax request, binding the click function every time to the div. I read a lot about .unbind("click") and .bind() it or similar( .off() ), but nothing worked for me. Maybe there is another solution for this problem?

it's triggered twice
Actually, not really. Put your console.log("Mouseclick"); in the first line of the click listener and you will see it only once per click.
$("#tiles").on('click','.category',function(e){
// …
//remove old elements
$('.category').fadeOut(200,function(){
console.log("Mouseclick");
$(this).remove();
//start request when animation completed
var nextrequest = new Request(tag1,tag2,tag3);
nextrequest.ajaxCategoryRequest();
});
});
The animation is your problem. The callback to fadeOut is invoked for each of the .category elements - that's why your $(this).remove() works (as this is only a single <div> element).
So you are actually starting a new request for each of the categories you have removed, which is of course not what you want. What you could do is moving the request out of the animation callback, then it will start right away. There are no race conditions, but - when the ajax is faster than 200 ms - it could happen that the new categories appear while the old ones are still fading out.
If you want to prevent such a glitch, you will need to find a way to fire the ajax after all of the animation callbacks have completed. Fortunately, there is the .promise method which does exactly that - it returns a promise which is guaranteed to resolve only once, and you can add callbacks for the end of the event queue:
$("#tiles").on('click','.category',function(e){
console.log("Mouseclick");
// …
// remove old elements
$('.category').fadeOut(200,function(){
$(this).remove();
}).promise().done(function() {
// start request once when all animations completed
var nextrequest = new Request(tag1,tag2,tag3);
nextrequest.ajaxCategoryRequest();
});
});
If you want to shorten the time until the ajax requests is finished, with promises you even can run the animation and the request in parallel very easily, adding a callback for when both tasks are finished:
function categoryRequest(data) {
return $.ajax({
type: "GET",
url: "request2.php",
data: {tag1: tag1, tag2: tag2, tag3: tag3},
dataType: "json" // invokes $.parseJSON automatically
});
}
function outputData(jdata) {
//Frage ausgeben
$("h1.question").html(jdata[0]);
//Tiles ausgeben
for (var i = 1; i < jdata.length; i++) {
$("#tiles").append(
'<div class="category" id="' + jdata[i].type + '" data-owntag="' + jdata[i].owntag + '" data-description="' + jdata[i].description + '"><img src="' + jdata[i].imageurl + '" alt="' + jdata[i].name + '"/><div class="ctitle">' + jdata[i].name + '</div></div>'
);
}
}
//Erster Aufruf
categoryRequest("", "", "").done(outputData);
// Weitere Aufrufe bei click
$("#tiles").on('click','.category',function(e){
console.log("Mouseclick");
//define next request variables (tags)
var stage = $(this).attr('id');
var owntag = $(this).data("owntag");
var tag1 = "", tag2 = "", tag3 = "";
if (stage == 'searchtype')
tag1 = owntag;
else if (stage == 'category')
tag2 = owntag;
else if (stage == 'subcategory')
tag3 = owntag;
else
console.log("No valid (stage)type defined");
var removed = $('.category').fadeOut(200,function(){
$(this).remove();
}).promise();
var request = categoryRequest(tag1,tag2,tag3);
$.when(request, removed).done(function(requestResults) {
outputData(requestResults[0]);
});
});

Use one method to trigger the function only for once.
$("#tiles").one('click','.category',function(){

$("#tiles").off('click').on('click','.category',function(){

I solutioned it like this
$('.category').fadeOut(200).promise().done(function(){
//start request when animation completed
$(".category").remove();
var nextrequest = new Request(tag1,tag2,tag3);
nextrequest.ajaxCategoryRequest();
});

Related

JQUERY ajax post - multiple click, one reload - it is possible?

I have a problem and I don't know what is the solution. I would like to reload the specified divs only once after multiple click. Now when I add new item to the database from dropdown input, then after each click each time reload the specified div, and sometimes it is very disturbing. When you want to select a new item from the list, and then suddenly reset, and you need to select again). How can I do that if I click to add new item (sometimes I select 4-5 new items - not multiple select!) then not refresh the specified div after each click, just once with a specified delay.
Here is the current code of the javascript part (now it refresh after 100 milliseconds after a new item added). I hope that someone could help me, or give me an idea how can I resolve this. Many thanks!
<script type="text/javascript">
$('body').on('click',".addhplayer",function() {
var absidplayer = $('#abshidplayer').find(":selected").val();
var abstype = $('#abshtype').find(":selected").val();
var obj = $(this); // first store $(this) in obj
var absseasonid = $(this).attr('data-absseasonid');
var absidclub = $(this).attr('data-absidclub');
var absidmatch = $(this).attr('data-absidmatch');
//var dataString = 'abstype=' + abstype + '&addplayer=1&' + 'absidplayer=' + absidplayer + '&' + 'absidclub=' + absidclub + '&' + 'absidmatch=' + absidmatch + '&' + 'absseasonid=' + absseasonid;
$.ajax({
url: 'edit_absence.php',
type: 'POST',
timeout: 100,
data: {
addtype: abstype,
addhplayer: '1',
addidplayer: absidplayer,
addidclub: absidclub,
addidmatch: absidmatch,
addseasonid: absseasonid
},
success: function(response, textStatus, jqXHR){
$('.hpstatus').show();
$(".hpstatus").load(" .hpstatus");
$('#injur').show();
$("#injur").load(" #injur");
$("#homelineups").load(" #homelineups");
$("#awaylineups").load(" #awaylineups");
},
});
});
</script>
check out my old response to this question :
How do you send an ajax request every time that a form input field changes?
basically wrap your event code to a delayed function, on multiple call it will cancel the previous planned ajax call if the delay is not reach
edit > on your particular code :
var changeTimer = false;
function yourSpecificEventCode(){
var absidplayer = $('#abshidplayer').find(":selected").val();
var abstype = $('#abshtype').find(":selected").val();
var $o = $(this); // first store $(this) in obj
var absseasonid = $o.attr('data-absseasonid');
var absidclub = $o.attr('data-absidclub');
var absidmatch = $o.attr('data-absidmatch');
$.ajax({
url: 'edit_absence.php',
type: 'POST',
timeout: 100,
data: {
addtype: abstype,
addhplayer: '1',
addidplayer: absidplayer,
addidclub: absidclub,
addidmatch: absidmatch,
addseasonid: absseasonid
},
success: function(response, textStatus, jqXHR){
$('.hpstatus').show().load(" .hpstatus");
$('#injur').show().load(" #injur");
$("#homelineups").load(" #homelineups");
$("#awaylineups").load(" #awaylineups");
},
});
}
$('body').on('click',".addhplayer",function() {
if(changeTimer !== false) clearTimeout(changeTimer);
let t = this ;
changeTimer = setTimeout(function(){
yourSpecificEventCode.call( t ) ;
changeTimer = false;
},300);
});

How to speed up my Ajax call when there are multiple same ajax call made on a page?

When the page loads, I am also dynamically creating a block . I am using ajax call to go and fetch data from another page and then populating it and creating my structure that is then added to a particular dom element. However, the problem is when I do this several times on the page during page loads, it takes quite some time for all Ajax call to finish. Do you know how I can speed up the ajax call?
$('.content-tile').each(function (idx, ele) {
// Step 1: Get the stuffs and add it in the right place on page load
var node_id = $(ele).find('article').attr('data-history-node-id');
$.get('/node/' + node_id , function (data) {
var $title = $(data).find('.title').text();
var $summary = $(data).find('.article__body').text();
var $ctaText = $(data).find('.article__field-read-more-text').text();
var $redirectToFile = $(data).find('.article__field-nova-redirect-to-file').find('a').attr('href');
var $redirectToLink = $(data).find('.article__field-redirect-link').find('a').attr('href');
// Either redirect to file or redirect to link in the order
var $ctaLinkHref = $redirectToFile;
if ($redirectToLink) {
$ctaLinkHref = $redirectToLink;
}
var $contentHover = "<a class='content-added contenthover hoveredLink' href= " + $ctaLinkHref + "></a>";
$(ele).find('article').after($contentHover); // Add the div that will be targeted for the hover
var $contentHoverHeader = "<h2 class='contenthover__header'>" + $title + '</h2>';
var $contentHoverContent = "<p class='contenthover__content'>" + $summary + '</p>';
var $contentHoverLink = "<a class='contenthover__link' href=" + $ctaLinkHref + '>' + $ctaText + '</a>';
$(ele).find('.contenthover').append($contentHoverHeader, $contentHoverContent, $contentHoverLink);
});
});
As Rory mentioned, instead of calling multiple times, just create the single object, post it back and return all the related data in one go.
// construct the array
var nodes = []
$('.content-tile').each(function (idx, ele) {
var node_id = $(ele).find('article').attr('data-history-node-id');
nodes.push(node_id);
}
// call ajax now
$.ajax({
url: "/node/RetrieveDataByNodes", //the new method which can accept the array and return data
data: JSON.stringify(nodes),
type: 'GET',
dataType: 'json'
}).done(function(result) {
$.each(result, function (k, v) {
//do something for each value
console.log(v);
}
});

Javascript dynamic switch doubling Ajax calls

I am having a problem with a jQuery click function. When a user clicks a HTML button, my jQuery dynamically loads some styled checkbox's/toggle switches based upon their corresponding on/off state stored in a database.
I then added a click function to each dynamically loaded toggle switch so that when a user clicks it, it updates the database with the new state, it then, with Ajax, calls the GetAllSwitches function again loading the current state of the switches from the DB back into the resultScreen.
It works, updates the state in the DB correctly, but the program remembers previous 'clicks' and runs them all again followed by the new click state every time a user clicks. So on the first click it makes 1 http request, 2nd 2, 3rd 4, 4th 8 etc. The problem being after a few clicks the ajax calls become huge.
I'm not that experienced in Javascript so I am aware my code is verbose and I am clearly missing something, but are there any fixes or better approaches to this?
In summary what I want to achieve is:
User clicks allSwitches
Ajax call to a database which returns all objects with a toggle switch on screen
Have the toggle switch's clickable which updates the database with new state
Allow the switches to be clicked as many times as the user likes only making one update to the DB
HTML
<fieldset>
<legend> Get All Lights Dynamically</legend>
<input type="button" value="Show All" id="allSwitches"/>
</fieldset>
<fieldset>
<div id='resultScreen'></div>
</fieldset>
JavaScript
$(document).ready(function(){
$("#allSwitches").click(function(){
$.ajax({
url: 'GetAll',
type: 'GET',
dataType: 'text',
success: function(data) {
getAllSwitches(data)
});
});
});
function getAllSwitches(data){
var tr;
myData = $.parseJSON(data);
for(var i = 0; i < myData.length; i++){
tr = $('<tr/>');
if(myData[i].state=="On"){
tr.append('<div id="togs' + i + '">' + '<label class="switch">' +
'<input type="checkbox" class="' + myData[i].lightName +'" checked>' +
'<div class="slider round"></div>'
+'</label>' + '</div>');
tr.append("<td>" + myData[i].lightName +
" is " + myData[i].state + "</td>");
$('#resultScreen').append(tr);
var className = '.' + myData[i]lightName;
var lightName = myData[i].lightName;
var state = "Off";
upTog(className, lightName, state);
} else if (myData[i].state=="Off"){
tr.append('<label class="switch">' +
'<input type="checkbox" class="' + myData[i].lightName +'" >' +
'<div class="slider round"></div>'
+'</label>');
tr.append("<td>" + myData[i].lightName +
" is " + myData[i].state + "</td>");
$('#resultScreen').append(tr);
var className = '.' + myData[i].lightName;
var lightName = myData[i].lightName;
var state = "On";
upTog(className, lightName, state);
}
}
}
function upTog(className, lightName, state){
$(document).on('click', className, function() {
$.ajax({
url: 'UpdateLight?lightName=' + lightName + "&state=" + state,
type: 'POST',
dataType: 'text',
success:function(data){
$.ajax({
url: 'GetAll',
type: 'GET',
dataType: 'text',
success: function(data) {
$('#resultScreen').empty();
getAllSwitches(data);
}});
}
})
});
}
Many thanks.
The easiest way to do it is to unbind the previous click before set the new one.
Change upToge() body like this:
$(className).unbind( "click" );
$(className).on('click', function () {
/* Your ajax call here */
});
You're adding the click handler to the className, which is not changing when you empty the #resultsScreen div. You can see how the handlers pile up in this jsbin: http://jsbin.com/hizigutogi/edit?js,console,output (fill the div, click the red box, empty it, fill it again, and click it a few more times)
Try passing the reference to the jQuery object tr into upTog and adding the click handler onto it directly, instead of attaching it to the class name.

How to get the value value of a button clicked Javascript or Jquery

I'll try to be as straight to the point as I can. Basically I using jquery and ajax to call a php script and display members from the database. Next to each members name there is a delete button. I want to make it so when you click the delete button, it deletes that user. And that user only. The trouble I am having is trying to click the value of from one delete button only. I'll post my code below. I have tried alot of things, and right now as you can see I am trying to change the hash value in the url to that member and then grap the value from the url. That is not working, the value never changes in the URL. So my question is how would I get the value of the member clicked.
<script type="text/javascript">
$(document).delegate("#user_manage", "pagecreate", function () {
$.mobile.showPageLoadingMsg()
var friends = new Array();
$.ajaxSetup({
cache: false
})
$.ajax({
url: 'http://example.com/test/www/user_lookup.php',
data: "",
dataType: 'json',
success: function (data) {
$.mobile.hidePageLoadingMsg();
var $member_friends = $('#user_list');
$member_friends.empty();
for (var i = 0, len = data.length; i < len; i++) {
$member_friends.append("<div class='user_container'><table><tr><td style='width:290px;font-size:15px;'>" + data[i].username + "</td><td style='width:290px;font-size:15px;'>" + data[i].email + "</td><td style='width:250px;font-size:15px;'>" + data[i].active + "</td><td><a href='#" + data[i].username + "' class='user_delete' data-role='none' onclick='showOptions();'>Options</a></td></tr><tr class='options_panel' style='display:none'><td><a href='#" + data[i].username + "' class='user_delete' data-role='none' onclick='showId();'>Delete</a> </td></tr></table></div>");
}
}
});
});
</script>
<script>
function showId() {
var url = document.URL;
var id = url.substring(url.lastIndexOf('#') + 1);
alert(id);
alert(url);
}
</script>
IDEAS:
1st: I think it would be easier to concatenate an string an later append it to the DOM element. It's faster.
2nd: on your button you can add an extra attribute with the user id of the database or something and send it on the ajax call. When getting the attribute from the button click, use
$(this).attr('data-id-user');
Why don't you construct the data in the PHP script? then you can put the index (unique variable in the database for each row) in the button onclick event. So the delete button would be:
<button onclick = "delete('indexnumber')">Delete</button>
then you can use that variable to send to another PHP script to remove it from the database.
$('body').on('click', 'a.user_delete', function() {
var url = document.URL;
var id = url.substring(url.lastIndexOf('#') + 1);
alert(id);
alert(url);
});
<?php echo $username ?>
Like wise if you pull down users over json you can encode this attribute like so when you create your markup in the callback function:
'<a href="#'+data[i].username+'" data-user-id="'+ data[i].username + '" class="user_delete" data-role="none" >Options</a>'
So given what you are already doing the whole scenerio should look something like:
$(document).delegate("#user_manage", "pagecreate", function () {
$.mobile.showPageLoadingMsg();
var friends = new Array(),
$member_friends = $('#user_list'),
// lets jsut make the mark up a string template that we can call replace on
// extra lines and concatenation added for readability
deleteUser = function (e) {
var $this = $(this),
userId = $this.attr('data-id-user'),
href = $this.attr('href'),
deleteUrl = '/delete_user.php';
alert(userId);
alert(href);
// your actual clientside code to delete might look like this assuming
// the serverside logic for a delete is in /delete_user.php
$.post(deleteUrl, {username: userId}, function(){
alert('User deleted successfully!');
});
},
showOptions = function (e) {
$(this).closest('tr.options_panel').show();
},
userTmpl = '<div id="__USERNAME__" class="user_container">'
+ '<table>'
+ '<tr>'
+ '<td style="width:290px;font-size:15px;">__USERNAME__</td>'
+ '<td style="width:290px;font-size:15px;">__EMAIL__</td>'
+ '<td style="width:250px;font-size:15px;">__ACTIVE__</td>'
+ '<td>Options</td>'
+ '</tr>'
+ '<tr class="options_panel" style="display:none">'
+ '<td>Delete</td>'
+ '</tr>'
+ <'/table>'
+ '</div>';
$.ajaxSetup({
cache: false
})
$(document).delegate('#user_manage #user_container user_options', 'click.userlookup', showOptions)
.delegate('#user_manage #user_container user_delete', 'click.userlookup', deleteUser);
$.ajax({
url: 'http://example.com/test/www/user_lookup.php',
data: "",
dataType: 'json',
success: function (data) {
$.mobile.hidePageLoadingMsg();
var markup;
$member_friends.empty();
for (var i = 0, len = data.length; i < len; i++) {
markup = userTmpl.replace('__USERNAME__', data[i].username)
.replace('__ACTIVE__', data[i].active)
.replace('__EMAIL__', data[i].email);
$member_friends.append(markup);
}
}
});
});
Here's a really simple change you could make:
Replace this part:
onclick='showId();'>Delete</a>
With this:
onclick='showId("+data[i].id+");'>Delete</a>
And here's the new showId function:
function showId(id) {
alert(id);
}

Array and while loop: make unique clickable

I have an array (via ajax) that looks like this:
data[i].id: gives the id of user i
data[i].name: gives the name of user i
I want to output the array like this:
X Leonardo Da Vinci
X Albert Einstein
X William Shakespeare
...
The X is an image (x.gif) that must be clickable. On click, it must go to functiontwo(), passing the parameter data[i].id. Functiontwo will open a jquery dialog with the question "Delete id data[i].id"?
I know this can't be too hard to do, but I can't seem to figure it out...
This is what I have so far:
function functionone() {
$.ajax({
type : 'POST',
url : 'post.php',
dataType : 'json',
success : function(data){
var message = "";
var i = 0;
while (i < (data.length - 1))
{
var myvar = data[i].id;
message = message + "<div class=" + data[i].id + "><img src=x.gif></div>" + data[i].name + "<br />";
$('#somediv').html(message).fadeIn('fast');
$("." + data[i].id + "").click(function () {
functiontwo(myvar);
});
i++;
}
}
});
}
function functiontwo(id) {
...}
I know why this isn't working. Var i gets populated again and again in the while loop. When the while loop stops, i is just a number (in this case the array length), and the jquery becomes (for example):
$("." + data[4].id + "").click(function () {
functiontwo(myvar);
});
, making only the last X clickable.
How can I fix this?
Thanks a lot!!!
EDIT:
This is my 2nd function:
function functiontwo(id) {
$("#dialogdelete").dialog("open");
$('#submitbutton').click(function () {
$('#submitbutton').hide();
$('.loading').show();
$.ajax({
type : 'POST',
url : 'delete.php',
dataType : 'json',
data: {
id : id
},
success : function(data){
var mess = data;
$('.loading').hide();
$('#message').html(mess).fadeIn('fast');
}
});
//cancel the submit button default behaviours
return false;
});
}
In delete.php there's nothing special, I used $_POST['id'].
As I pointed out in my comment. The problem is the .click part. Either use bind, or use a class for all the elements, and a click-event like this $('.classnamehere').live('click',function () { // stuff });
function functionone() {
$.ajax({
type : 'POST',
url : 'post.php',
dataType : 'json',
success : function(data){
var message = "";
var i = 0;
while (i < (data.length - 1))
{
var myvar = data[i].id;
message = message + "<div class=\"clickable\" id=" + data[i].id + "><img src=x.gif></div>" + data[i].name + "<br />";
$('#somediv').html(message).fadeIn('fast');
i++;
}
}
});
}
$('.clickable').live('click',function () {
alert($(this).attr('id') + ' this is your ID');
});
The usual trick is create a separate function to create the event handler. The separate function will receive i as a parameter and the generated event will be able to keep this variable for itself
make_event_handler(name){
return function(){
functiontwo(name);
};
}
...
$("." + data[i].id + "").click( make_event_handler(myvar) );

Categories

Resources