I tried to create a simple solution for ordering problem in async calls on a project I'm working on.
The best solution I found was this:
I attach an event that check if the pre-requirements are done, if so I remove the event listener and perform the function.
Each function calls the event once its done and that way everyone will be waiting until the can run.
This is the code:
$(function() {
$(document).on('bmadone',function() {
if(beepmeapp.done_arr['fb-init']){
$(document).off('bmadone','#bma-root',this);
getBMAUserRoutes();
}
});
});
The function (for the test) is doing this:
function getBMAUserRoutes(){
$.ajax({
url : '/bma/users/fb',
type : 'GET',
data : {
access_token: 'abc'
},
success : function( data ) {
console.log('getBMAUser: success');
beepmeapp.done_arr['user-init'] = true;
$('#bma-root').trigger('bmadone');
},
error : function( xhr, err ) {
console.log('getBMAUser: error');
}
});
}
It works great but the problem I have is that it gets into a loop that never ends.
Don't know why but it looks like:
$(document).off('bmadone','#bma-root',this);
Doesn't remove the listener...
I'm a true newbie in JS / Jquery and all of this client side development so I guess I'm probably missing something basic.
You have to pass the same (sub)set of parameters to .off that you passed to .on. I.e. in your case it would be $(document).off('bmadone').
You never passed '#bma-root' to .on and this does not refer to the event handler (the function you pass to .on), so by passing those you won't remove the correct event handler.
If you need to remove only that specific handler, you can use a named function instead:
function handler() {
if(beepmeapp.done_arr['fb-init']){
$(document).off('bmadone', handler);
getBMAUserRoutes();
}
}
$(document).on('bmadone', handler);
// or as a named function expression
$(document).on('bmadone', function handler() {
if(beepmeapp.done_arr['fb-init']){
$(document).off('bmadone', handler);
getBMAUserRoutes();
}
});
or use namespaced events.
Related
I use jquery-ujs for ajax requests (data-remote="true"). My problem is, that the first request is okay, and while the second is running, it breaks. Whenever I call in the events $('#modal').empty() or $('#modal').text('') or $('#modal').html(''), no more events are going to be called.
To be clear, here's the code:
$(document).on('ajax:beforeSend', function () {
console.log('beforeSend');
$('#modal').empty().modal('show');
});
$(document).on('ajax:send', function () {
console.log('send');
});
$(document).on('ajax:success', function (e, xhr) {
console.log('success');
$('#modal').html(xhr).drags({ handle: '.modal-header' }).modal('show');
if (typeof doWork === 'function') {
doWork();
}
});
$(document).on('ajax:complete', function () {
console.log('complete');
});
And the console output:
beforeSend
send
success
complete
beforeSend
If I move $('#modal').empty().modal('show'); to the send event, then it is going to be called, but the success method is never called anymore (neither error neither complete, nothing more).
This problem annoys me for more hours now... Thank you for your help.
How about to move empty() to the ajax:complete?
In this case, when your modal closes, it opens empty for the next use and is ready for reuse.
I suggest that you put this command in the last step so that it does not cause any problems. Like the following:
$(document).on('ajax:complete', function () { console.log('complete'); . . . . $('#modal').empty(); });
I believe that this is not the best solution and there are definitely other solutions. But I hope this solution will solve your problem.
$('#modal').on('hidden.bs.modal', function () {
$(this).empty()
})
My workaround was to avoid using jquery-ujs events and using global ajax events, so my final working code is:
$(document).ajaxSend(function () {
$('#modal').empty().modal('show');
});
$(document).ajaxSuccess(function (e, xhr) {
$('#modal').html(xhr.responseText).drags({ handle: '.modal-header' }).modal('show');
if (typeof doWork === 'function') {
doWork();
}
});
I have a non-jquery script with an addEventListener on a buttonclick. This works fine the first run but I want it to change "sendingurl" to some other value after the first click. (the succesfull insert of form becomes an update form). However, even though sendingurl fills with the new id value, it doesnt change after the event is fired again. Instead by the second click it fires the newly created event tohether with the old one with the old value.
The resulting values in console.log:
1st click: event "click" is fired with url: input.php
2nd click:
event "click" is fired with url: input.php
event "click" is fired with url: update.php?io=items&id=693
So I want to get rid of the input being triggered after the first click. Does someone know how to solve this?
var itemid = getHash();
ini(prepare); // using window.onload to execute
function prepare() {
if (itemid) {
// update
var sendingurl = 'update.php?io=items&id=' + itemid;
} else {
// input
var sendingurl = 'input.php';
}
// submitevent
æ($("submitBtn"), 'click', function() {
console.log("event \"click\" is fired with url: " + sendingurl);
var json = new FormData(document.forms[0]);
ajax(sendingurl, json, submittedInput);
});
}
// callback function after ajax did his magic
function submittedInput(response) {
if (response) {
if (!itemid) {
itemid = response;
prepare();
}
} else {
$("status").innerHTML = "something went wrong with the input";
}
}
// function to add events without the use of jquery or prototype
function æ(el, evType, fn, useCapture) {
if (el.addEventListener) {
el.removeEventListener(evType, fn, useCapture);
el.addEventListener(evType, fn, useCapture);
return true;
} else if (el.attachEvent) {
el.detachEvent('on' + evType, fn);
var r = el.attachEvent('on' + evType, fn);
return r;
} else {
el['on' + evType] = fn;
}
}
The first time through, there is no event handler to remove and your anonymous function is added as an event handler. The anonymous function creates as closure, so the value of sendingurl is now "constant" no matter how the external sendingurl changes. That is why you are getting the old value.
The second time through, the event handler function being passed to removeEventListener is not yet tied to an event because the second anonymous function is not the same function as the anonymous function on the first pass. So no event handler is removed. You then add a second event with the new anonymous function with the revised value of sendingurl. That is why you are seeing both functions fire.
Convert your anonymous function to a normal function and use the normal function name instead.
I have an event listener setup using jQuery like this:
$(document).on('my-custom-event', function(e, custom_id) {
console.log('the ID is: ' + custom_id);
});
It is easy to trigger this with jQuery.trigger, like this:
$(document).trigger('my-custom-event', '12345');
But I am trying to figure out how to trigger it with vanilla javascript, and also ensure that custom_id gets passed properly.
For example, this does not work. It triggers the event, but does not pass the custom_id argument:
var e = new CustomEvent('my-custom-event', 'asdf')
document.dispatchEvent(e)
How can I use plain javascript to trigger the event and also pass the custom_id argument?
According to MDN, detail can be used to pass data when initializing the event.
Here is an example:
// add listener
// let's be cool and add it to the document for this example
document.addEventListener('EVENT-NAME', function (e) {
// do something
// use e.detail for the data
});
// create event
var event = new CustomEvent('EVENT-NAME', {
detail: 'legit data'
});
// dispatch event
document.dispatchEvent(event);
I have also created a demo: http://jsfiddle.net/dirtyd77/02p2o7ut/1/
Hopefully this helps and let me know if you have any other questions!
EDIT:
Unfortunately, there is no way (that I know of) to do what you are asking, however, there is a workaround.
jQuery events have a property called originalEvent. You could check to see if that value exists in the event that custom_id does not. Here is how you could do this:
DEMO: http://jsfiddle.net/dirtyd77/02p2o7ut/2/
$(document).on('my-custom-event', function (e, custom_id) {
// if custom_id not defined, will check e.originalEvent.detail
custom_id = custom_id || e.originalEvent.detail;
console.log('the ID is: ' + custom_id);
});
// trigger jQuery event
$(document).trigger('my-custom-event', '12345');
$('#customEvent').click(function () {
// create event
var event = new CustomEvent('my-custom-event', { detail: '12345' });
// dispatch event
document.dispatchEvent(event);
});
I'm building a simple jQuery plugin called magicForm (How ridiculous is this?). Now face to a problem that I think I'm not figuring out properly.
My plugin is supposed to be applied on a container element, that will show each of its inputs one by one as user fills them. That's not the exact purpose of my problem.
Each time I initialize the container, I declare an event click callback. Let me show an example.
(function($){
var methods = {
init: function(options){
return this.each(function(){
var form, inputs;
var settings = {
debug: false
};
settings = $.extend(settings, options);
form = $(this);
$('a.submit', form).on('click', function(event){
if (settings.submitCallback) {
settings.submitCallback.call(form, inputs);
}
return false;
});
});
},
reset: function() {
}
}
$.fn.magicForm = function(method) {
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist.' );
}
};
})($);
I'm focusing on a specific part of this code :
$('a.submit', form).on('click', function(event){
if (settings.submitCallback) {
settings.submitCallback.call(form, inputs);
}
return false;
});
Because each time the init method is called, that poor callback is registered.
I was experiencing this painfully, when I invoked my plugin on an element nested in a twitter bootstrap 'tab', nested itself in a bootstrap modal :
I was calling init each time the event 'shown' of my bootstrap modal was triggered.
So, this is how I fixed it in my init method :
// Prevent callback cumulation
if (!$(this).data('form_initialized')) {
$('a.submit', form).on('click', function(event){
if (settings.submitCallback) {
settings.submitCallback.call(form, inputs);
}
return false;
});
$(this).data('form_initialized', true);
}
And I'm far from feeling sure about this.
Thank your for your time !
Many jquery plugins use data to know if their plugins were initialized. Most often, they use the name of their own plugin as a part (or in whole) as the data. For example:
$(this).data('magicForm')
So your approach of using that to signal is not a bad one.
However, you have two other options:
1) Pull the event handler out so the handler is a single instance. Above your methods, do var fnOnSubmit = function() { ... } Then you can simply ensure proper binding by calling $('a.submit', form).unbind('click', fnOnSubmit) before rebinding it the way you are already doing it.
2) Another option is to use event namespaces.
$('a.submit', form).unbind('click.magicForm'); then rebinding it with .on('click.magicForm') This namespace approach ensures that when you unbind it only unbinds in the context of your namespace magicForm, thus leaving all other click events (e.g. from other plugins) intact.
I hope this helps.
You could first explicitely remove the click-handler:
$('a.submit', form).off('click').on('click', function(event){ ... })
However, I would suggest you use event namespacing to prevent all click handlers (even those perhaps set by code not your own) from being removed:
$('a.submit', form).off('click.magicForm').on('click.magicForm', function(event){ ... })
I have a problem with event object passed to the function in drop event. In my code, div#dropArea has it's drop event handled by firstDrop function which does some animations and then calls the proper function dropFromDesktop which handles the e.dataTransfer.files object. I need this approach in two separate functions because the latter is also used further by some other divs in the HTML document (no need to duplicate the code). First one is used only once, to hide some 'welcome' texts.
Generally, this mechanism lets you drag files from desktop and drop them into an area on my website.
Here's, how it looks (in a shortcut):
function firstDrop(ev) {
var $this = $(this);
//when I call the function here, it passes the event with files inside it
//dropFromDesktop.call($this, ev);
$this.children('.welcomeText').animate({
opacity: '0',
height: '0'
}, 700, function() {
$('#raw .menu').first().slideDown('fast', function() {
//when I call the function here, it passes the event, but 'files' object is empty
dropFromDesktop.call($this, ev);
});
});
}
function dropFromDesktop(ev) {
var files = ev.originalEvent.dataTransfer.files;
(...) //handling the files
}
$('#dropArea').one('drop', firstDrop);
$('some_other_div').on('drop', dropFromDesktop);
The problem is somewhere in jQuery.animation's callback - when I call my function inside it, the event object is passed correctly, but files object from dataTransfer is empty!
Whole script is put inside $(document).ready(function() { ... }); so the order of function declarations doesn't matter, I guess.
I suspect your problem is related with the lifetime of the Event object. Unfortunately, I have no clue about the cause of it. But, there is a way to workaround it that I can think of and it is keeping a reference to Event.dataTransfer.files instead.
var handleFileList = function(fn) {
return function(evt) {
evt.preventDefault();
return fn.call(this, evt.originalEvent.dataTransfer.files);
};
};
var firstDrop = function(fileList) { ... }
var dropFromDesktop = function(fileList) { ... }
$('#dropArea').one('drop', handleFileList(firstDrop));
$('some_other_div').on('drop', handleFileList(dropFromDesktop));