js jquery forward saved event - javascript

I´m holding back some of my native click events on hyperlinks, to check if the result page holds a content.
Im saving the jquery event object and after some checkings, i want to let the event go its natural way.
Capture Event
Check for Contents
If contents available
forward event as it was fired
else
do nothin
At this moment, I just saving the "href" property and want to set it to the document.location.href if true comes back.
Now, the question: Is there a better way to forward/reraise the existing event, than setting the href to the document.location.href?

Using document.location.href would be fine, and also seems like the simplest option to me.
But just for the sake of exploring other options, you could also have js click the link for you if it's deemed as safe. For example, something like this.
$('a').click(function() {
if( !$(this).is('.content-verified') ) {
var self = this;
// Do your content checking here, with the callback for verified good
// content being $(self).has_good_content();
return false;
}
return true;
});
// Callback for good content
// should be something like this:
$.fn.has_good_content = function() {
return $(this).each(function() {
$(self).addClass('content-verified');
$(self).click();
});
};

This sounds like a job for the jQuery Deferred object. New in jQuery 1.5+
function done() {
var dfd = $.Deferred(),
timeout;
timeout = setInterval(function() {
if (contents available) {
clearInterval(timeout);
return dfd.resolve();
}
}, 50);
return dfd.promise();
}
$('#my-link').bind('click', function(e) {
e.preventDefault();
var $this = $(this);
$.when(done())
.then(function(o) {
//unbind the click event that prevents the default action and click on the link afterward
$this.unbind('click').click();
});
});
So what is happening is it will wait for the resolve/success state from the done function. You are telling your click event to wait because done is using the Deferred object and has promised to return something.
I have put a setInterval to check every 50 mili seconds if the contents have loaded then I resolve the Deferred object therefore the then in click event will be called.
You can pass an object to dfd.resolve(); like this dfd.resolve({ test: true });. Then the o argument in then will have o.test .
I have used Deferred several times and I really liked it.
Hope this helps

Related

What is the preferred pattern for re-binding jQuery-style UI interfaces after AJAX load?

This always gets me. After initializing all lovely UI elements on a web page, I load some content in (either into a modal or tabs for example) and the newly loaded content does not have the UI elements initialized. eg:
$('a.button').button(); // jquery ui button as an example
$('select').chosen(); // chosen ui as another example
$('#content').load('/uri'); // content is not styled :(
My current approach is to create a registry of elements that need binding:
var uiRegistry = {
registry: [],
push: function (func) { this.registry.push(func) },
apply: function (scope) {
$.each(uiRegistry.registry, function (i, func) {
func(scope);
});
}
};
uiRegistry.push(function (scope) {
$('a.button', scope).button();
$('select', scope).chosen();
});
uiRegistry.apply('body'); // content gets styled as per usual
$('#content').load('/uri', function () {
uiRegistry.apply($(this)); // content gets styled :)
});
I can't be the only person with this problem, so are there any better patterns for doing this?
My answer is basically the same as the one you outline, but I use jquery events to trigger the setup code. I call it the "moddom" event.
When I load the new content, I trigger my event on the parent:
parent.append(newcode).trigger('moddom');
In the widget, I look for that event:
$.on('moddom', function(ev) {
$(ev.target).find('.myselector')
})
This is oversimplified to illustrate the event method.
In reality, I wrap it in a function domInit, which takes a selector and a callback argument. It calls the callback whenever a new element that matches the selector is found - with a jquery element as the first argument.
So in my widget code, I can do this:
domInit('.myselector', function(myelement) {
myelement.css('color', 'blue');
})
domInit sets data on the element in question "domInit" which is a registry of the functions that have already been applied.
My full domInit function:
window.domInit = function(select, once, callback) {
var apply, done;
done = false;
apply = function() {
var applied, el;
el = $(this);
if (once && !done) {
done = true;
}
applied = el.data('domInit') || {};
if (applied[callback]) {
return;
}
applied[callback] = true;
el.data('domInit', applied);
callback(el);
};
$(select).each(apply);
$(document).on('moddom', function(ev) {
if (done) {
return;
}
$(ev.target).find(select).each(apply);
});
};
Now we just have to remember to trigger the 'moddom' event whenever we make dom changes.
You could simplify this if you don't need the "once" functionality, which is a pretty rare edge case. It calls the callback only once. For example if you are going to do something global when any element that matches is found - but it only needs to happen once. Simplified without done parameter:
window.domInit = function(select, callback) {
var apply;
apply = function() {
var applied, el;
el = $(this);
applied = el.data('domInit') || {};
if (applied[callback]) {
return;
}
applied[callback] = true;
el.data('domInit', applied);
callback(el);
};
$(select).each(apply);
$(document).on('moddom', function(ev) {
$(ev.target).find(select).each(apply);
});
};
It seems to me browsers should have a way to receive a callback when the dom changes, but I have never heard of such a thing.
best approach will be to wrap all the ui code in a function -even better a separate file -
and on ajax load just specify that function as a call back ..
here is a small example
let's say you have code that bind the text fields with class someclass-for-date to a date picker then your code would look like this ..
$('.someclass-for-date').datepicker();
here is what i think is best
function datepickerUi(){
$('.someclass-for-date').datepicker();
}
and here is what the load should look like
$('#content').load('/uri', function(){
datepickerUi();
})
or you can load it at the end of your html in script tag .. (but i dont like that , cuz it's harder to debug)
here is some tips
keep your code and css styles as clean as possible .. meaning that for text fields that should be date pickers give them one class all over your website ..
at this rate all of your code will be clean and easy to maintain ..
read more on OOCss this will clear what i mean.
mostly with jquery it's all about organization ... give it some thought and you will get what you want done with one line of code ..
edit
here is a js fiddle with something similar to your but i guess it's a bit cleaner click here

Pause JavaScript function

I need to pause a JavaScript function execution in the middle and then resume it after a button click. Please help me.
This isn't possible.
Break the function up in to two parts, run one, and have the other assigned to the click event handler of the button.
You could use a pop up box.
alert("Pausing to get Coffee");
Like David said, it is not possible to stop execution of a function in Javascript (well, not at the moment anyway). One solution would be this :
** EDITED ** after you added some precision to what you wanted to do
// pass the reference of the button id to the function
function showConfirm(message, callback) {
// 1. check if the lightbox is not already created, if not create it
// 2. keep a reference to your key elements, for example, your buttons
var btnOk = document.getElementById('btnOk'); // ...for example
var btnCancel = document.getElementById('btnCancel'); // ...for example
// 3. have a 'cleanup' function so you can dismiss your lightbox, unregister
// any events/callbacks, etc.
var cleanup = function() {
// 6. hide lightbox
// 7. remove events/callbacks, etc.
btnOk.click = null; // for example
btnCancel.click = null; // for example
// etc.
};
// 4. update your lightbox with the message given in argument
// 5. register some events to your buttons...
btnOk.click = function() {
callback(true); // ok was pressed
cleanup();
};
btnCancel.click = function() {
callback(false); // cancel was pressed
cleanup();
}
}
All you have to remember is that, in Javascript, everything should be asynchronous. If your function should return a value, it should be a function that does not require long to execute. As soon as you read "user input" with Javascript, you need callbacks. You might want to take a look at how other lightbox implementations are done, especially in frameworks like JQuery, etc.

How can I chain click events with Mootools so that they execute in order?

I have a series of buttons that fire the list function when they are clicked. The list function itself contains an AJAX request and a bunch of other stuff before and after which loads in a separate section of the page.
var list = function() { }
$$('.buttons').addEvent('click', list);
Everything works fine if I wait for list to complete before clicking on another button. However, if I click on two buttons quickly, the page will start to return incorrect responses. In fact, it appears as though the responses get out of sync by 1. So if I quickly click on button A then button B, it will load response A. Then if I click (much later) on button C, it will load response B.
There are two ways I can see to solve this:
1) Remove the click event from other buttons when any button is clicked and then restore it when list is complete. Unfortunately, I have tried placing $$('.buttons').removeEvents() at the top of the list function and then $$('.buttons').addEvent('click', list); at the bottom but this has no effect on the problem.
2) Chain the click events so that list is only ever executed when the preceding list has finished.
So can anybody tell me how to get the second solution working? Additionally, if anybody knows why the first solution doesn't work and why I get the weird delayed AJAX response behaviour, that would be great!
The first solution doesn't work because events on an element are fired in order, but are executed asynchronously. You'll need to setup a queue of callbacks that you can process when the event is triggered.
Here's the basic idea:
addQueuedEvent = function(node, event, callback) {
if ( typeof callback != "function" ) {
throw "Callback must be a function";
}
event = event.toLowerCase();
var eventQueue = "_" + event;
if ( !node.hasOwnProperty(eventQueue) ) {
node[eventQueue] = [];
}
node[eventQueue].push(callback)
};
processEventQueue = function(node, event) {
var eventQueue = "_" + event;
if ( node.hasOwnProperty(eventQueue) ) {
for ( var i=0, l=node[eventQueue].length; i<l; ++i ) {
node[eventQueue][i]();
}
}
};
And the usage:
var someElement = $("#some-element");
addQueuedEvent(someElement, "click", callback1);
addQueuedEvent(someElement, "click", callback2);
addQueuedEvent(someElement, "click", callback3);
someElement.addEvent("click", function() {
processEventQueue(this, "click");
});
The syntax checks out, but this is not tested. Hope that helps.
i would personally just set a global / scoped variable in your class or whatever - something like 'isClicked = false'.
then simply check at the the the click event function, something like:
var isClicked = false, click = function() {
if (isClicked)
return false;
isClicked = true;
// ... do stuff, chained or otherwise...
// when done, make click function work again:
isClicked = false; // you can do this onComplete on the fx class also if you use it
};
i would go against chaining events with effects - if you have an animation going on, simply wait for it to end--otherwise it can get messy for any trigger happy user that thinks double clicking is the way to go. an alternative is to stop / cancel any effects that are taking place on a new click. for instance, you can stop any tweens etc through FX by something like:
if (isClicked === true) fxinstance.cancel();
http://mootools.net/docs/core/Fx/Fx
the other thing you can do is look at the mootools .chain class
http://mootools.net/docs/core/Class/Class.Extras#Chain
and also, on any fx instances, you can pass on link: "chain" and simply queue them up.
good luck

How to tell whether the $(window).load()/window.onload event has already fired?

I have a script that is being inserted dynamically via another script. The code in that script is wrapped inside the $(window).load() event because it requires the images on the page to have all loaded. In some browsers it works fine, but in others it seems not to fire because the page has already finished loading by the time the code is run.
Is there any way to check and see whether the page has already finished loading - either via jQuery or JavaScript? (including images)
In this situation, I don't have access to the onload event of the original document (aside from altering it via the loaded script - but that would seem to present the same problem).
Any ideas/solutions/advice would be greatly appreciated!
You could try setting up a handler that's invoked via a timeout that will check the images to see if their properties are available. Clear the timer in the load event handler so if the load event occurs first, the timer won't fire. If the properties aren't available, then the load event hasn't fired yet and you know that your handler will eventually be invoked. If they are, then you know that the load event occurred before your handler was set and you can simply proceed.
Pseudocode
var timer = null;
$(function() {
$(window).load( function() {
if (timer) {
clearTimeout(timer);
timer = null;
}
process();
});
timer = setTimeout( function() {
if (checkAvailable())
process();
}
}, 10*1000 ); // waits 10 seconds before checking
});
function checkAvailable()
{
var available = true;
$('img').each( function() {
try {
if (this.height == 0) {
available = false;
return false;
}
}
catch (e) {
available = false;
return false;
}
});
return available;
}
function process() {
... do the real work here
}
I wrote a plugin that may be of some use: http://plugins.jquery.com/project/window-loaded
I think your problem would resolve itself if you'd use $(document).ready instead of $(window).load - see the jquery documentation.
You guys should read this:
http://web.enavu.com/daily-tip/daily-tip-difference-between-document-ready-and-window-load-in-jquery/
Don't know if this is what you are after, but have you tried(?):
$(document).ready(function(){
...
});
http://docs.jquery.com/Events/ready#fn

How can I return true/false from an animation queue thats inside a click() function?

This seems so tricky to me that I think I am overlooking something simple here.
Can you help me find it?
Basically, the situation is this
(variables where the name is not important are named with a capital letter) :
$('a').filter('somecriteria').each(function() {
var self = $(this);
self.click(function() {
B.animate({ something: somevalue }, { duration: "fast", complete: function(){
return true; // <--- see notes below
}
});
return false;
});
});
Notes:
How can I let this return be the return that is sent from click() so that the user is taken to the new page only after animate() has fully completed (NB: in my real code I have three animate()'s chained together).
I need to do this because the animation is cut off and the new page is loaded prematurely. The animation only comes to full effect when return false is sent from click() until the animation really ends.
I tried capturing the href attribute of the link and doing a window.location = capturedlink instead of return true.
This works, but if possible I want to avoid that because some people have disabled changing location because of security concerns.
Or is there another way of keeping jQuery changing to a new URL in the middle of an animation?
Thank you very much for checking out this question.
Andre
I'm afraid capturing the href way would be the most obvious way of doing what you want. I am not sure if doing the following would circumvent people that disabled changing location in their security settings, but you could try it:
$('a').filter('somecriteria').each(function() {
var self = $(this);
self.click(function() {
if(self.hasClass('animated')) return true; // return true if already animated
B.animate({ something: somevalue }, { duration: "fast", complete: function(){
self.addClass('animated').click(); // fire the click event
}
});
return false;
});
});

Categories

Resources