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);
});
Related
I'm trying to add a clientEvent filter to an already working AJAX fullCalendar. The idea is to allow the visitor to filter the events already displayed by selecting a choice in a droping list.
The code is currently as follows:
jQuery(document).ready(function($) {
$('#calendar').fullCalendar({
events: function(start, end, timezone, callback) {
$.post(
MyAjax.ajaxurl,
{
action: 'get_fullcalendar',
data: {
slotbegin: start.unix(), // données à compléter
slotend: end.unix()
}
},
function( events ) {
callback( events );
}
);
},
eventRender: function(event, element) {
element.qtip({
id: 'eventdetails',
content: {
text: event.image + event.description,
title: event.title
},
});
}
});
$("#cible_select").change(function() {
var cible = $(this).val()
var events = $('#calendar').fullCalendar('clientEvents', function(evt) {
return evt.public_cible == cible;
});
});
});
The fullCalendar works OK by itself. But I don't know how to integrate the clientEvents bit so it is used when the user makes change to the #cible_select selector.
I've been trying many things for the past hours, and would appreciate some help to solve this issue.
Thanks a lot for any hint.
This function might help you. call this function where ever you want.
function parseClientEvents(/*pass params here*/){
var clientArr = $('#calendar').fullCalendar('clientEvents');
for(i in clientArr){
console.log(clientArr[i]);
//all your logic goes here.
}
return true;
}
I seem to have misunderstood the way clientEvents works. I thought it would re-display the whole calendar with the selected events only, but that's not the case.
removeEvents works to hide/suppress events one doesn't want any more, but those are not available on the client side any more, so refetchEvents has to be used if the user changes his mind and makes another choice.
removeEventSource works only if you have a limited number of sources, and I want to be able to combine several filters, so there is quite a number of combinations.
So, I'm completely rethinking my filtering strategy: clientEvents is definitely not the way to toggle on/off events on a multicriteria basis.
I'm trying to get the hang of using ajax loads (mostly via jquery) to make my site more efficient. Wondering if anyone can provide any suggestions re "best practices" for using ajax?
Is there a way to simplify a script for multiple ajax calls? For example, I currently have the working script:
$(document).ready(function() {
$('#dog').click(function () {
$('#body').load("dog.html");
});
$('#cat').click(function () {
$('#body').load("cat.html");
});
$('#bird').click(function () {
$('#body').load("bird.html");
});
$('#lizard').click(function () {
$('#body').load("lizard.html");
});
});
The script just gets longer and longer with each additional function. Is there a simpler, more efficient way to write this script to cover multiple load scripts?
Also, should I be using ajax loads to replace the majority of actual links?
Here is a suggestion, since the code you posted seems to have a pattern between the id and the filename:
$(document).ready(function () {
$(document).on('click', 'commonParentElementHere', function (e) {
$('#body').load(e.target.id + ".html");
});
});
This suggestion uses .on() and you just need to add a commonParentElementHere, a id or a class of the common parent of those elements.
Another option is to use a class on all elements that should be clickable, and then use the code passing the id to the html file name, like:
$(document).ready(function () {
$(document).on('click', '.theCOmmonClass', function () {
$('#body').load(this.id + ".html");
});
});
I'd say give all the elements you want to click on a class say ajax then.
$(document).ready(function() {
$('.ajax').click(function () {
$('#body').load(this.id + ".html");
});
});
Assuming that the id matches the file name the script can be simplified to:
$(document).ready(function() {
$('#dog,#cat,#bird,#lizard').click(function () {
var fileName = this.id + ".html";
$('#body').load(fileName);
});
});
This script simply specifies each id in a single selector that separates each id with a comma. This will calls the click function to be fired for each element. With the anonymous function attached to the click event, the id of each element is obtained and concatenated to create the file name which is then passed to the load function.
If the id doesn't always match the element you could use the following approach.
var mappings = [
{id: "fileName1", file:"file.html"},
{id: "fileName2", file:"file2.html"}
];
$(document).ready(function() {
for(var i = 0; i < mappings; i++){
createMapping(mappings[i]);
}
function createMapping(mapping){
$("#" + mapping.id).click(function(){
$('#body').load(mapping.file);
});
}
});
$('.collapse').each(function() {
var title= $(this).siblings('.accordion-heading').find('a');
$(this).on('show hide', function (e) {
if(!$(this).is(e.target))return;
title.parent().toggleClass('active', 300);
title.parent().hasClass('active') ? $('input.party').prop('value', '') : $('input.party').val(title.siblings('.delete').prop('id'));
var id = title.siblings('.delete').prop('id');
var data = {id: id};
$.post("times.php", data, function(result) {
if(title.parent().hasClass('active')){
$('.times').html('');
} else {
$('.times').html($.parseJSON(result));
}
})
})
})
So I am adding a new accordion-group to my html by adding a new party and I wan't all this to work on the newly added elements as well. I didn't find topics that could help me since it is a bit more specific than any random each function (I think).
This future elements thing is new to me, so I would appreciate some explanations or a good link to a place other that the jquery website which I already checked.
Thank you for your time!
Basically what I want to do this replace $(this).on('show hide', function (e) { with something like $(document).on('show hide', $(this), function (e) {. What I just wrote doesn't work though.
If it is just about the event handler, then you can use event delegation to capture the event on dynamically created elements as well.
There is not reason why you have to use .each here, so just omit it:
$(document.body).on('show hide', '.collapse', function() {
var title = $(this).siblings('.accordion-heading').find('a');
if(!$(this).is(e.target))return;
// rest of the code...
});
this will apply on any new objects matching selector
jQuery(document).on('show hide', '.accordion-heading a', function(event){
...
});
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;
}
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".