jquery .submit live click runs more than once - ajax

I use the following code to run my form ajax requests but when i use the live selector on a button i can see the ajax response fire 1 time, then if i re-try it 2 times, 3 times, 4 times and so on...
I use .live because i also have a feature to add a post and that appears instantly so the user can remove it without refreshing the page...
Then this leads to the above problem... using .click could solve this but it's not the ideal solution i'm looking for...
jQuery.fn.postAjax = function(success_callback, show_confirm) {
this.submit(function(e) {
e.preventDefault();
if (show_confirm == true) {
if (confirm('Are you sure you want to delete this item? You can\'t undo this.')) {
$.post(this.action, $(this).serialize(), $.proxy(success_callback, this));
}
} else {
$.post(this.action, $(this).serialize(), $.proxy(success_callback, this));
}
return false;
})
return this;
};
$(document).ready(function() {
$(".delete_button").live('click', function() {
$(this).parent().postAjax(function(data) {
if (data.error == true) {
} else {
}
}, true);
});
});​
EDIT: temporary solution is to change
this.submit(function(e) {
to
this.unbind('submit').bind('submit',function(e) {
the problem is how can i protect it for real because people who know how to use Firebug or the same tool on other browsers can easily alter my Javascript code and re-create the problem

If you don't want a new click event bound every time you click the button you need to unbind the event before re-binding it or you end up with multiple bindings.
To unbind events bound with live() you can use die(). I think the syntax using die() with live() is similar to this (untested):
$(document).ready(function(){
$('.delete_button').die('click').live('click', function(){
$(this).parent().postAjax(function(data){
if (data.error == true){
}else{
}
}, true);
});
});
However, if you are using jQuery 1.7 or later use on() instead of live() as live() has been deprecated since 1.7 and has many drawbacks.
See documentation for all the details.
To use on() you can bind like this (I'm assuming the delete_button is a dynamically added element) :
$(document).ready(function(){
$(document).off('click', '.delete_button').on('click', '.delete_button', function(){
$(this).parent().postAjax(function(data){
if (data.error == true){
}else{
}
}, true);
});
});
If you are using an earlier version of jQuery you can use undelegate() or unbind() and delegate() instead. I believe the syntax would be similar to on() above.
Edit (29-Aug-2012)
the problem is how can i protect it for real because people who know
how to use Firebug or the same tool on other browsers can easily alter
my Javascript code and re-create the problem
You can some-what protect your scripts but you cannot prevent anyone from executing their own custom scripts against your site.
To at least protect your own scripts to some degree you can:
Write any script in an external js file and include a reference to that in your site
Minify your files for release
Write any script in an external js file and include a reference to that in your site
That will make your html clean and leave no trace of the scripts. A user can off course see the script reference and follow that for that you can minify the files for release.
To include a reference to a script file:
<script type="text/javascript" src="/scripts/myscript.js"></script>
<script type="text/javascript" src="/scripts/myscript.min.js"></script>
Minify your files for release
Minifying your script files will remove any redundant spacing and shorten function names to letters and so no. Similar to the minified version of JQuery. The code still works but it is meaningless. Off course, the hard-core user could follow meaningless named code and eventually figure out what you are doing. However, unless you are worth hacking into I doubt anyone would bother on the average site.
Personally I have not gone through the minification process but here are some resources:
Wikipedia - Minification (programming)
Combine, minify and compress JavaScript files to load ASP.NET pages faster
How to minify (not obfuscate) your JavaScript using PHP
Edit (01-Sep-2012)
In response to adeneo's comment regarding the use of one().
I know you already found a solution to your problem by unbinding and rebinding to the submit event.
I believe though it is worth to include a mentioning of one() in this answer for completeness as binding an event with one() only executes the event ones and then unbinds itself again.
As your click event, when triggered, re-loads and rebinds itself anyway one() as an alternative to unbinding and re-binding would make sense too.
The syntax for that would be similar to on(), keeping the dynamic element in mind.
// Syntax should be right but not tested.
$(document).ready(function() {
$(document).one('click', '.delete_button', function() {
$(this).parent().postAjax(function(data) {
if (data.error == true) {} else {}
}, true);
});
});​
Related Resources
live()
die()
on()
off()
unbind()
delegate()
undelegate()
one()
EDIT AGAIN !!!! :
jQuery.fn.postAjax = function(show_confirm, success_callback) {
this.off('submit').on('submit', function(e) { //this is the problem, binding the submit function multiple times
e.preventDefault();
if (show_confirm) {
if (confirm('Are you sure you want to delete this item? You can\'t undo this.')) {
$.post(this.action, $(this).serialize(), $.proxy(success_callback, this));
}
} else {
$.post(this.action, $(this).serialize(), $.proxy(success_callback, this));
}
});
return this;
};
$(document).ready(function() {
$(this).on('click', '.delete_button', function(e) {
$(e.target.form).postAjax(true, function(data) {
if (data.error) {
} else {
}
});
});
});​

jQuery.fn.postAjax = function(success_callback, show_confirm) {
this.bind( 'submit.confirmCallback', //give your function a namespace to avoid removing other callbacks
function(e) {
$(this).unbind('submit.confirmCallback');
e.preventDefault();
if (show_confirm === true) {
if (confirm('Are you sure you want to delete this item? You can\'t undo this.')) {
$.post(this.action, $(this).serialize(), $.proxy(success_callback, this));
}
} else {
$.post(this.action, $(this).serialize(), $.proxy(success_callback, this));
}
return false;
})
return this;
};
$(document).ready(function() {
$(".delete_button").live('click', function() {
$(this).parent().postAjax(function(data) {
if (data.error == true) {
} else {
}
}, true);
});
});​
As for the "people could use Firebug to alter my javascript" argument, it does not hold : people can also see the request that is sent by your $.post(...), and send it twice.
You do not have control over what happens in the browser, and should protect your server side treatment, rather than hoping that "it won't show twice in the browser, so it will prevent my database from being corrupt".

Related

how to use callback when I click ajax in nightmarejs

lately I have been studing nightmare module I think it's very simple and useful but I have question.
how to use callback when I click ajax button
MyCode
var Nightmare = require('nightmare'),
nightmare = Nightmare();
nightmare
.goto('https://motul.lubricantadvisor.com/Default.aspx?data=1&lang=ENG&lang=eng')
.click('input[title="Cars"]')
.wait(1000)
.evaluate(function () {
//return $('#ctl00_ContentPlaceHolder1_lstModel option');
var links = document.querySelectorAll('#ctl00_ContentPlaceHolder1_lstMake option');
return [].map.call(links, function (e) {
return {value: e.value, name: e.text};
});
})
.end()
.then(function (items) {
console.log(items);
});
there is wait method. most people use wait methed I searched googling
.wait(1000)
I don't use wait method. because If it's network disconnect or slow. It's not good code
Could you help me callback method??
Thanks. So I have motify the code but It's doesn't work
var Nightmare = require('nightmare'),
nightmare = Nightmare();
nightmare
.goto('https://motul.lubricantadvisor.com/Default.aspx?data=1&lang=ENG&lang=eng')
.click('input[title="Cars"]')
.wait('#result > #ctl00_ContentPlaceHolder1_lstMake option')
.evaluate(function () {
$(document).ajaxSuccess(function () {
var links = document.querySelectorAll('#ctl00_ContentPlaceHolder1_lstMake option');
return [].map.call(links, function (e) {
return {value: e.value, name: e.text};
});
});
})
.end()
.then(function (items) {
console.log(items);
});
There are many ways to solve this. The easiest would be the following.
Suppose when an Ajax request finishes, it always changes something on the page. Most of these changes can be easily detected when waiting for specific elements to appear which can be matched by CSS selectors.
Let's say you click something and the result is written into the element matched by "#result". If there wasn't such an element before the click then you can wait until the existence of this element:
.click("button")
.wait("#result")
// TODO: do something with the result
You can also use CSS selectors to count things. For example, let's say there are ten elements that can be matched with "#result > a". If a click adds 10 more, then you can wait for the 20th using:
.click("button")
.wait("#result > a:nth-of-type(20)")
// TODO: do something with the result
The world of CSS selectors is pretty big.
Of course, you could use evaluate to add a general Ajax event handler like $(document).ajaxSuccess(fn) to be notified whenever some callback finished, but the source code of a page changes all the time. It would be easier to maintain your code if you would look for the results that can be seen in the DOM.
Use this, ajax callback..
$.ajax(url,{dataType: "json", type: "POST" })
.then(function successCallback( data ) { //successCallback
console.log(data);
}, function errorCallback(err) { //errorCallback
console.log(err);
});
// console.log(2);
});

Prototype.js event observe click intercept and stop propagation

I have a page that is built around a wrapper with some very defined logic. There is a Save button on the bottom of the wrapped form that looks like this:
<form>
... my page goes here...
<input id="submitBtnSaveId" type="button" onclick="submitPage('save', 'auto', event)" value="Save">
</form>
This cannot change...
Now, I'm writing some javascript into the page that gets loaded in "...my page goes here...". The code loads great and runs as expected. It does some work around the form elements and I've even injected some on-page validation. This is where I'm stuck. I'm trying to "intercept" the onclick and stop the page from calling "submitPage()" if the validation fails. I'm using prototype.js, so I've tried all variations and combinations like this:
document.observe("dom:loaded", function() {
Element.observe('submitBtnSaveId', 'click', function (e) {
console.log('Noticed a submit taking place... please make it stop!');
//validateForm(e);
Event.stop(e);
e.stopPropagation();
e.cancelBubble = true;
console.log(e);
alert('Stop the default submit!');
return false;
}, false);
});
Nothing stops the "submitPage()" from being called! The observe actually works and triggers the console message and shows the alert for a second. Then the "submitPage()" kicks in and everything goes bye-bye. I've removed the onclick attached to the button in Firebug, and my validation and alert all work as intended, so it leads me to think that the propagation isn't really being stopped for the onclick?
What am I missing?
So based on the fact that you can't change the HTML - here's an idea.
leave your current javascript as is to catch the click event - but add this to the dom:loaded event
$('submitBtnSaveId').writeAttribute('onclick',null);
this will remove the onclick attribute so hopefully the event wont be called
so your javascript will look like this
document.observe("dom:loaded", function() {
$('submitBtnSaveId').writeAttribute('onclick',null);
Element.observe('submitBtnSaveId', 'click', function (e) {
console.log('Noticed a submit taking place... please make it stop!');
//validateForm(e);
Event.stop(e);
e.stopPropagation();
e.cancelBubble = true;
console.log(e);
alert('Stop the default submit!');
return false;
submitPage('save', 'auto', e);
//run submitPage() if all is good
}, false);
});
I took the idea presented by Geek Num 88 and extended it to fully meet my need. I didn't know about the ability to overwrite the attribute, which was great! The problem continued to be that I needed to run submitPage() if all is good, and that method's parameters and call could be different per page. That ended up being trickier than just a simple call on success. Here's my final code:
document.observe("dom:loaded", function() {
var allButtons = $$('input[type=button]');
allButtons.each(function (oneButton) {
if (oneButton.value === 'Save') {
var originalSubmit = oneButton.readAttribute('onclick');
var originalMethod = getMethodName(originalSubmit);
var originalParameters = getMethodParameters(originalSubmit);
oneButton.writeAttribute('onclick', null);
Element.observe(oneButton, 'click', function (e) {
if (validateForm(e)) {
return window[originalMethod].apply(this, originalParameters || []);
}
}, false);
}
});
});
function getMethodName(theMethod) {
return theMethod.substring(0, theMethod.indexOf('('))
}
function getMethodParameters(theMethod) {
var parameterCommaDelimited = theMethod.substring(theMethod.indexOf('(') + 1, theMethod.indexOf(')'));
var parameterArray = parameterCommaDelimited.split(",");
var finalParamArray = [];
parameterArray.forEach(function(oneParam) {
finalParamArray.push(oneParam.trim().replace("'","", 'g'));
});
return finalParamArray;
}

Click on link to hide all other content divs

Example: (werkwijze is custom, i also have the same code with other names like -contact)
$(function() {
$('#activator-werkwijze').click(function(){
$('#overlay-werkwijze').fadeIn('fast',function(){
$('#box-werkwijze').animate({'bottom':'0px'},800);
});
});
$('#boxclose-werkwijze').click(function(){
$('#box-werkwijze').animate({'bottom':'-600px'},800,function(){
$('#overlay-werkwijze').fadeOut('fast');
});
});
});
activator shows the content, boxclose closes the content.
clicking all activators opens all content while it needs to open only one and close the others..
edit got it fixed:
This is the script which works, (the overlay is useless) jsfiddle.net/8y7Sr/126/
You need to provide more information about how you are calling the pop-up script. If you are using jquery, you can just close all and then open one
$('.className').slideDown(200);
$('#specificItem').slideUp(200);
But again, you should explain how you are doing the calls in more detail for a better answer
example, put inside function jQuery
if($('.hide').css('display')=='block'){ $('.hide').slideUp(); }else{ $('.hide').slideDown(); }
$(function() {
$('.activator').each(function(){
$(this).click(function(){
showHideAnimation(clickedObj);
});
});
}
function showHideAnimation(clickedObj)
{
$('.activator').each(function(){
if($(this).id == clickedObj.id) {
$(this).fadeIn('fast',function(){
$(this).animate({'bottom':'0px'},800);
});
} else {
$(this).animate({'bottom':'-600px'},800,function(){
$(this).fadeOut('fast');
});
}
}
}
Please try this if it doesn't work properly then replace $(this) with only this

Return false not working for jQuery live

Well this has me well and truly stumped. After searching for the last few hours I still cannot seem to work out where I am going wrong.
I am trying to append an AJAX response to a container when it gets clicked. That works fine but I don't want it to append another object when the elements from the AJAX response also gets clicked.... so:
<div id="container">
<!-- AJAX response to get inserted here, for example -->
<span id="ajaxResponse"></span>
</div>
Here is my script:
$('#container').click(function(e) {
var current_el = $(this).get(0);
$.ajax({
url: 'text.html',
success: function(data) {
$(current_el).append(data);
}
});
return false;
});
So it works fine but for some reason the click event on #container also fires when I click on the AJAX response span!?
According to jQuery documentation:
To stop further handlers from
executing after one bound using
.live(), the handler must return
false. Calling .stopPropagation() will
not accomplish this.
But unless I am mistaken, I am calling false? :(
Anyone help me out on this?
UPDATED:
So the only way I can get it to work is by updating my code to this:
$('#container').live('click', function() {
var current_el = $(this).get(0);
$.ajax({
url: 'text.html',
success: function(data) {
$(current_el).append(data);
}
});
});
$('#ajaxResponse').live('click', function(e) {
return false;
});
This seems a little messy though... anyone have a better solution?
Where is live part you mention in the title of the question ?
It is how the event model works.. If you click on element which does not handle the event, the event will travel up the DOM hierarchy until it finds an element that handles the click (and stops its propagation..). Otherwise you would not be able to put an image inside a <a> tag and click on it..
You can bind a canceling handler on the inner element assuming you have someway to target it..
$.ajax({
url: 'text.html',
success: function(data) {
$(current_el).append(data);
// assuming the returned data from ajax are wrapped in tags
$(current_el).children().click(function(){ return false;});
}
});
I think the return false is referring to something else in this case...
you should try calling stopPropagation() - this should stop the "click" function from propagating down to the ajaxResponse span....
One option that you may want to try is switching over to using live(). Essentially, the click event you setup is calling bind(), and the solution you referenced is using live() which is a variation on bind().
For example:
$('#container').live("click", function(e) {
var current_el = $(this).get(0);
$.ajax({
url: 'text.html',
success: function(data) {
$(current_el).append(data);
}
});
return false;
});
HTH

Why are the jQuery functions only working the first time they're called?

There is a link that, when clicked, toggles between loading HTML into a div and emptying the div. When the div is clicked to load the html, I use the jQuery ajax load() function. When the text is loading, I want to display "Please wait...", so I tried using the jQuery ajaxStart() and ajaxStop() methods, but they only seemed to work the first time the load() function was called. So I switched to ajaxSend() and ajaxSuccess, but that also only seems to work the first time the load function is called. What's wrong?
HTML:
<p id="toggleDetail" class="link">Toggle Inspection Detail</p>
<p id="wait"></p>
<div id="inspectionDetail"></div>
jQuery:
$(
function(){
$('#toggleDetail').click(function(){
if($.trim($('#inspectionDetail').text()).length)
{
$('#inspectionDetail').empty();
}
else
{
$('#inspectionDetail').load('srInspectionDetailFiller.cfm');
}
});
}
);
$(
function(){
$('#wait').ajaxSend(function() {
$(this).text('Please wait...');
});
}
);
$(
function(){
$('#wait').ajaxSuccess(function() {
$(this).text('');
});
}
);
You should put up the 'Please wait...' message in your click function, then clear the message upon successful completion of your load:
$('#toggleDetail').click(function(){
if($.trim($('#inspectionDetail').text()).length)
{
$('#inspectionDetail').empty();
}
else
{
$('#wait').text('Please wait...');
$('#inspectionDetail').load('srInspectionDetailFiller.cfm', function() {
$('#wait').text('');
});
}
});
Edit: Although ajaxSend should technically work here, I don't recommend it. With ajaxSend, "All ajaxSend handlers are invoked, regardless of what Ajax request is to be sent". It seem overkill to me to hook all Ajax requests on the page which you're really only trying to deal with this single click.

Resources