I have the following function:
Test.prototype.showToken = function () {
$('#modal').modal('show');
$('#modal').find('#btnOk').click(function (e) {
// here I want to callback
var returnValue = '123';
});
$('#modal').find('#btnProcess').click(function (e) {
// here I want to callback cancel
var returnValue = '456';
});
},
Now I have this in other function:
$.Test.showToken();
This works fine... Now I want inside my showToken to have a CallBack so when the click button happens I get in my other function the callback triggered. For example:
$.Test.showToken(function(e){
// here would like to get the trigger when the btnOK is clicked and also be able to get the returnValue.
// here would like to get the trigger when the btnProcess is clicked and also be able to get the returnValue.
});
Any clue?
You can accept callback functions in your function arguments.
Test.prototype.showToken = function (options) {
$('#modal').modal('show');
$('#modal').find('#btnOk').click(options.okButtonCallback);
$('#modal').find('#btnProcess').click(options.processButtonCallback);
}
Then when you call your function, you can pass callbacks to do special things.
$.Test.showToken({okButtonCallback: function(){
// This will run on the ok button press.
}, processButtonCallback: function(){
// This will run on the process button press.
}});
Is this what you want?
Test.prototype.showToken = function (okCallback, processCallback) {
$('#modal').modal('show');
$('#modal').find('#btnOk').click(function (e) {
// here I want to callback
var returnValue= ???
okCallback(returnValue)
});
$('#modal').find('#btnProcess').click(function (e) {
// here I want to callback cancel
processCallback()
});
},
/// ...
/// Usage:
Test.showToken(function(retValFromOk){
// from OK
console.log("From ok", retValFromOk);
}, function() {
// from process
});
Related
I am trying to use callbacks in order to effectively "overwrite" the standard alert and confirm actions in JavaScript.
The code I am using is a bit long winded so I jotted it into a working jsfiddle
I am trying to get it so that a callback is used to determine true or false, but it is coming back as undefined as the callback function is fired before a click is
My questions, is how can I change this to effectively overcome the functions value being returned before the click is called via jQuery?
Example usage:
<button onclick="confirm('This is a test')">Show dialog (confirm)</button>
Example jQuery events:
if (confirm("This is a test")) {
alert("Confirmed")
}
else {
alert("Did not confirm")
}
Edit
Using a loop within the callback messed it us a lot...
You are mixing things up when waiting for the return value.
You are passing dialog.checkForInput as the callback. But in the dialog.show() function you do:
var ret = callback();
...
return ret;
But the dialog.checkForInput function doesn't return anything, it merely sets event listeners.
As events all run "asynchronously" it would be more sensible to give your dialog a callback function which will be run when there actually would be an event. Meaning: in your checkForInput function (I would name it differently, but whatever) run the callback and pass the action as a parameter. Something like:
checkForInput: function () {
$(document).ready(function () {
$(".dialog_confirm_okay").on("click", function () {
dialog.hide();
callback('confirm');
})
$(".dialog_confirm_cancel").on("click", function () {
dialog.hide();
callback('cancel');
})
$(".dialog_alert_okay").on("click", function () {
dialog.hide();
callback('alert');
})
})
}
And your callback could look like this (assuming your callback was called dialogCallback):
function dialogCallback ( action ) {
alert('Dialog closed with action: ' + action);
};
Some points I conclude from your code:
The reason why statement callback() return undefined value is because dialog.checkForInput return nothing.
The $(document).ready inside checkForInput is async, so returned value from that block is meaningless (it won't become the return value of the checkForInput as well).
And also you put the return statement inside event declaration, it'll become return value of the event (when the event triggered), not the checkForInput.
I did some modification on your code, this one working. Basically I create new method called onclick, which will be called every time button yes or no is clicked.
show: function (e_type, e_content) {
var d = dialog;
var d_head = e_type == "confirm" ? "Confirm Action" : "Error";
var d_buttons = e_type = "confirm" ? d.parts.buttons.okay + d.parts.buttons.cancel : d.dparts.buttons.alert_okay;
var _dialog = d.parts.main + d.parts.head.replace("{DIV_HEADER}", d_head) + d.parts.body + e_content + "<div class='dialog_button_container'>" + d_buttons + "</div>" + d.parts.footer;
$("body").append(_dialog);
},
onclick: function (ret) {
$(".errors").text("Return value was: " + ret);
},
showError: function (e_content) {
dialog.show("alert", e_content);
dialog.checkForInput();
},
showConfirm: function (e_content) {
dialog.show("confirm", e_content);
dialog.checkForInput();
},
checkForInput: function () {
var self = this;
$(".dialog_confirm_okay").on("click", function () {
dialog.hide();
self.onclick(true);
})
$(".dialog_confirm_no").on("click", function () {
dialog.hide();
self.onclick(false);
})
$(".dialog_alert_okay").on("click", function () {
dialog.hide();
self.onclick(false);
})
},
Working example: https://jsfiddle.net/p83uLeop/1/
Hope this will help you.
EDITED
From the comment section I assume that you want this alert to become a blocking function like window.confirm, so you can do something like if (confirm('Are you sure')).
But sadly it's impossible to achieve this case.
I have some suggestion, you can encapsulate your code better, and implement clean callbacks or promises. Maybe something like this:
showConfirm(function (ok) {
if (ok) {
// "yes" clicked
} else {
// "no" clicked
}
})
// or
showConfirm(function () {
// "yes" clicked
}, function () {
// "no clicked"
})
// or
var customConfirm = showConfirm()
customConfirm.on('yes', function () {
// "yes" clicked
})
customConfirm.on('no', function () {
// "no" clicked
})
Say I have an existing button and attach a click to it via jQuery:
var $button = $('#test').click(function () { console.log('original function') });
Now, say I want to override that click so that I can add some logic to the function before and after it. I have tried binding and wrapping using the functions below.
Function.prototype.bind = function () {
var fn = this;
var args = Array.prototype.slice.call(arguments);
var object = args.shift();
return function () {
return fn.apply(object, args.concat(Array.prototype.slice.call(arguments)));
}
}
function wrap(object, method, wrapper) {
var fn = object[method];
return object[method] = function() {
return wrapper.apply(this, [fn.bind(this)].concat(
Array.prototype.slice.call(arguments)));
}
}
so I call wrap with the object that the method is a property of, the method and an anonymous function that I want to execute instead. I thought:
wrap($button 'click', function (click) {
console.log('do stuff before original function');
click();
console.log('do stuff after original function');
});
This only calls the original function. I have used this approach on a method of an object before with success. Something like: See this Plunker
Can anyone help me do this with my specific example please?
Thanks
You could create a jQuery function that gets the original event handler function from data, removes the click event, then adds a new event handler. This function would have two parameters (each functions) of before and after handlers.
$(function() {
jQuery.fn.wrapClick = function(before, after) {
// Get and store the original click handler.
// TODO: add a conditional to check if click event exists.
var _orgClick = $._data(this[0], 'events').click[0].handler,
_self = this;
// Remove click event from object.
_self.off('click');
// Add new click event with before and after functions.
return _self.click(function() {
before.call(_self);
_orgClick.call(_self);
after.call(_self);
});
};
var $btn = $('.btn').click(function() {
console.log('original click');
});
$btn.wrapClick(function() {
console.log('before click');
}, function() {
console.log('after click');
});
});
Here is a Codepen
After a long search I reached the same answer as #Corey, here is a similar way of doing it considering multiple events:
function wrap(object, method, wrapper) {
var arr = []
var events = $._data(object[0], 'events')
if(events[method] && events[method].length > 0){ // add all functions to array
events[method].forEach(function(obj){
arr.push(obj.handler)
})
}
if(arr.length){
function processAll(){ // process all original functions in the right order
arr.forEach(function(func){
func.call(object)
})
}
object.off(method).on(method, function(e){wrapper.call(object,processAll)}) //unregister previous events and call new method passing old methods
}
}
$(function(){
$('#test').click(function () { console.log('original function 1') });
var $button = $('#test').click(function () { console.log('original function 2') });
wrap($button, 'click', function (click,e) {
console.log('do stuff before original functions');
click()
console.log('do stuff after original functions');
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id='test'>click me</div>
So when someone hits Reply, I am attempting to pop-up a form to type your response. Once the form is submitted, it disappears until the next time you hit Reply.
This is working except after the 1st time, I am submitting the information twice. If I do it a third time, the form submits three times. Essentially what is happening is the previous form doesn't seem to be resetting after I hide it again.
I checked this website/google and have tried using reset() but it didn't work. Below is the code:
$(document).on('click', '.secretfeed button', function () {
var message_id = $(this).attr('name');
$(".comment_box").show();
$("#m_id").val(message_id);
var value = document.getElementById("m_id").value;
$('#comment_form').submit(function (e) {
e.preventDefault();
var commentData = $(this).serialize();
$.post('../../process_comment.php', commentData, processData);
function processData(data) {
//$('comment_form').reset()
$(".comment_box").hide();
$('#comment_form')[0].reset();
RefreshFeed();
}
});
});
Rather than initializing the submit function on every click, move it outside the click function. jQuery may be creating an instance of it for each click.
$('#comment_form').submit(function (e) {
e.preventDefault();
var commentData = $(this).serialize();
$.post('../../process_comment.php', commentData, processData);
function processData(data) {
//$('comment_form').reset()
$(".comment_box").hide();
$('#comment_form')[0].reset();
RefreshFeed();
}
});
$(document).on('click', '.secretfeed button', function () {
var message_id = $(this).attr('name');
$(".comment_box").show();
$("#m_id").val(message_id);
var value = $("#m_id").val();
});
The alternative is to unbind the click function before reusing it.
We want a reusable way to handle the state. We will save the state of the button in a boolean which gets turned on and off depending on the status of the request. The pattern is the following:
var isSending = false;
function onSubmit() {
isSending = true;
// Send data
}
function onComplete() {
// done sending data
isSending = false;
}
if (!isSending) {
onSubmit();
}
// When data sending is finished:
onComplete();
The above can be encapsulated in a more functional way that uses promises to manage the state. (jQuery AJAX functions all return a promise-like object):
function oneAtATimeFunction(promisedFunction) {
var pendingPromise;
function reset() { pendingPromise = null; }
return function() {
if (pendingPromise) { return pendingPromise; }
pendingPromise = promisedFunction.apply(promisedFunction, arguments)
.always(reset);
return pendingPromise;
}
}
function submitForm() {
return $.ajax({
url: '/foo',
method: 'POST',
data: { data: 'from form' }
});
}
$('#submit-button').on('click', oneAtATimeFunction(submitForm));
Adding a little flare to the UI We can add a way to turn on and off the submit button. First we will define a helper function to handle the on and off state:
function buttonEnable(enabled) {
$('#submit-button').attr('disabled', !enabled);
}
buttonEnable(false); // disable the button
buttonEnable(true); // enable the button
Putting it all together:
function onClick() {
buttonEnable(false);
return onSubmit()
.always($.proxy(buttonEnable, null, true));
// The above is also the same as:
// .always(function() { buttonEnable(true); });
}
$('#submit-button').on('click', oneAtATimeFunction(onClick));
To see this in action here is a JSBin example.
I am using the following (http://jsfiddle.net/mkmurray/drv5w/27/) code to allow me to override the .show() function of a DIV.
<script>
(function ($) {
var _oldShow = $.fn.show;
$.fn.show = function (/*speed, easing, callback*/) {
var argsArray = Array.prototype.slice.call(arguments),
duration = argsArray[0],
easing,
callback,
callbackArgIndex;
// jQuery recursively calls show sometimes; we shouldn't
// handle such situations. Pass it to original show method.
if (!this.selector) {
_oldShow.apply(this, argsArray);
return this;
}
if (argsArray.length === 2) {
if ($.isFunction(argsArray[1])) {
callback = argsArray[1];
callbackArgIndex = 1;
} else {
easing = argsArray[1];
}
} else if (argsArray.length === 3) {
easing = argsArray[1];
callback = argsArray[2];
callbackArgIndex = 2;
}
return $(this).each(function () {
var obj = $(this),
oldCallback = callback,
newCallback = function () {
if ($.isFunction(oldCallback)) {
oldCallback.apply(obj);
}
obj.trigger('afterShow');
};
if (callback) {
argsArray[callbackArgIndex] = newCallback;
} else {
argsArray.push(newCallback);
}
obj.trigger('beforeShow');
_oldShow.apply(obj, argsArray);
});
};
})(jQuery);
</script>
I have the following HTML code
<div id="divBeforeHiddenDiv">
foo
</div>
<div id="hiddenDiv" style="display:none">
bar
</div>
And then:
<script>
$('#hiddendiv').bind("beforeShow", function () {
alert("show event successfully overridden");
});
</script>
It works great when I call $('#hiddenDiv').show() but not if I call $('#divBeforeHiddenDiv').next().show() the hidden div containing 'bar' shows but the alert is not displayed.
So why?
UPDATE
This appears to be a jQuery issue as per Bergi's comment. If I use this JSFiddle on jQuery 1.7.1 it works but using jQuery 1.10.1 or any higher version it does not: JSFiddle. Is there a better solution than simply downgrading?
You need to bind the events to the proper elements.
From the example you've given, and what I've interpreted, this piece of code
$('#beforeShow').bind("beforeShow", function () {
alert("show event successfully overridden");
});
Should be
$('#hiddenDiv').bind("beforeShow", function () {
alert("show event successfully overridden");
});
As you want the events to be bound to the hidden div. (or as described in the question, the div right after "#divBeforeHiddenDiv"
You also should change this piece
$('divBeforeHiddenDiv').next().show()
to this
$('#divBeforeHiddenDiv').next().show()
divBeforeHiddenDiv is an ID and in the first code snippet there is no id in the jQuery object.
JSFiddle
I am finding it difficult to synchronize blur and click events. The scenario is as follows: I have a page in which i have a textbox and a button. Now I have a blur event handler for the textbox, which basically makes an AJAX request and updates some part of the page. Also I have a click handler for the button which does some other job.
Now the problem here is that since I have a blur event handler on the textbox, when I enter something in the text box and directly click the button, it fires both blur and click events (as expected). The problem is synchronizing the two since the click handler should only execute once the blur handler has returned (if there was any blur event).
Sample code is as follows:
$('#textbox').on('blur', function(){
//make an ajax request
});
$('#btn').on('click',function(){
//wait for the blur event to finish(if any)
// then execute the code
})
Try something like this:
http://jsfiddle.net/8m7j5/2/
var blurring = [];
var expecting = false;
$(document).ready(function () {
$('#textbox').on('blur', function () {
// make an ajax request
console.log("TEXTBOX BLURRED");
blurring.push(1); // Flag a new blur event
$.ajax({
url: "/echo/json/",
complete: function () {
setTimeout(function () { // Simulate AJAX request taking 2 seconds
console.log("AJAX REQUEST COMPLETE");
blurring.pop(); // Flag completed blur event
checkClick();
}, 2000);
}
});
});
$('#btn').on('click', function () {
// wait for the blur event to finish(if any)
// then execute the code
console.log("ACTUALLY CLICKED BUTTON");
expecting = true;
checkClick();
});
});
function checkClick() {
if (expecting && blurring.length < 1) {
console.log("CHECKING: EXPECTING CLICK AND NO MORE BLUR EVENTS");
expecting = false;
clickFunction();
} else {
console.log("CHECKING: EITHER NOT EXPECTING OR BLUR EVENT(S) STILL OCCURRING");
}
}
function clickFunction() {
console.log("EXECUTING CLICK FUNCTION");
// Put your actual click code here
}
What you actually want to happen when the button is clicked, put in clickFunction.
The code from ianpgall a bit improved:
var functionsToCall = [];
var ajaxRunning = false;
$(document).ready(function(){
$('#textbox').on('blur', function(){
ajaxRunning = true;
console.log("START AJAX REQUEST");
$.ajax({
url: "/echo/json/",
complete: function () {
setTimeout(function () { // Simulate AJAX request taking 2 seconds
console.log("AJAX REQUEST COMPLETE");
ajaxRunning = false;
fireStackedCalls();
}, 5000);
}
});
})
$('#btn').on('click',function(){
if(ajaxRunning === true) {
functionsToCall.push('clickFunction()');
} else {
clickFunction();
}
});
function fireStackedCalls() {
while(functionsToCall.length > 0) {
toCall = functionsToCall.pop();
eval(toCall);
}
}
function clickFunction() {
console.log("EXECUTING CLICK FUNCTION");
// Put your actual click code here
}
});
Now every call of the clickFunction is recorded and executed if the ajax request is done.