jQuery UI Widget After Load Event - javascript

I am trying to create a custom jQuery UI Widget. The widget contains resources that take a bit longer to load, so I would like to fire an event after everything has finished rendering on the page.
I have tried the following two methods but neither have worked:
_initializeEvents: function() {
this._on(this.element, {
load: function() {
//Do stuff
}
});
}
and
_initializeEvents: function () {
this.element.load(function() {
//do stuff
});
}
What can I do to capture an event after the widget has loaded. I'd rather not set a timeout.

5 Years late, but I had a similar issue and did this to solve it.
// Get an instance of your widget
var WidgetInstance = $("#ID");
// Take a copy of the original method
var tmp = YourWidgetName.YourMethodName;
// Extend the method with some extra code
WidgetInstance.YourMethodName = function () {
// This line will run your current method
tmp.apply(this, arguments);
// This code will run after that has completed
console.log("Kaboom");
};

Related

Observe a JS event, when you only know PART of the event name?

I've inherited some JS (that I can't change) that fires a bunch of events:
jQuery(document).trigger('section:' + section);
// where "section" changes dynamically
And I want to observe for ALL of these events, and parse out the value for section, and do something different depending on it's contents.
If it didn't change I could do this:
jQuery(document).on('section:top', doStuff );
But how do I observe an event if I only know the first part of that event name?
You cannot listen for all events in the style of $().on('section:*'), unfortunately. If you can change the code, I would do the following:
jQuery(document).trigger({
type: 'section',
section: section
});
Then you listen for it and don't need to parse anything out
jQuery(document).on('section', function(e){
if (e.section === 'top') {
// Something happened to the top section
}
});
If you want to minimize your code changes, leave the old event in there, that way existing code will be unaffected.
A different approach would be to use event namespaces.
jQuery(document).trigger('section.' + section);
jQuery(document).on('section', function(e){
if (e.namespace === 'top') {
// Something happened to the top section
}
});
I, however, prefer the first approach because event namespaces are most commonly used for a different purpose: to be able to remove events without being forced to keep a reference to the handler itself. See http://css-tricks.com/namespaced-events-jquery/ and http://ejohn.org/apps/workshop/adv-talk/#13. I prefer to use styles that other developers are used to, if they do the job.
I'm really not sure about your use case but you could overwrite $.fn.trigger method:
(function ($) {
var oldTrigger = $.fn.trigger;
$.fn.trigger = function () {
if (arguments[0].match(/^section:/)) {
doStuff(arguments[0].split(':')[1]);
}
return oldTrigger.apply(this, arguments);
};
})(jQuery);
var section = "top";
jQuery(document).trigger('section:' + section);
function doStuff(section) {
alert(section);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Here's what I ended up doing.
It's a combination of Juan Mendes's solution, and using a method from the prototype library
Originally, there was a function that ran this code:
myObject.adjustSection(section) {
jQuery(document).trigger('section:' + section);
}
// I couldn't edit this function
So I extended the function with prototype's wrap method, since my project used prototype as well as jQuery.
// My custom function wrapper
// extend adjustSection to include new event trigger
myObject.prototype.adjustSection = myObject.prototype.adjustSection.wrap(
function(parentFunction, section) {
// call original function
parentFunction(section);
// fire event w/section info
jQuery(document).trigger({
type: 'adjustSection',
section: section
});
}
);
Then, it runs the original one, but also fires my custom event that includes the section info.
Now, I can do this to observe that event and get the section type:
jQuery(document).on('adjustSection', function(event) {
event.section; // contains the section I need
});
Of course, this means I have to utilize both prototype and jquery within the same scope, which isn't the best thing in the world. But it worked.

jQuery when element becomes visible [duplicate]

This question already has answers here:
How to check if element is visible after scrolling?
(46 answers)
Closed 1 year ago.
Basically, I am wondering if there is a way to automatically run a function when an element becomes hidden or visible, not on a user click but automatically in another script.
I don't want this to just run one time, because the elements (such as a slider) constantly change from visible to hidden.
Would this be something that jQuery can do with bind? Such as binding the element's visibility to a function (I don't know how to write this)
If you need me to elaborate more on what I'm trying to do, let me know. Thanks
Pseudocode:
$('#element').bind('display:none', function);
function(){
//do something when element is display:none
}
$('#element').bind('display:block', function2);
function2(){
//do opposite of function
}
There are no events in JQuery to detect css changes.
Refer here: onHide() type event in jQuery
It is possible:
DOM L2 Events module defines mutation events; one of them - DOMAttrModified is the one you need. Granted, these are not widely implemented, but are supported in at least Gecko and Opera browsers.
Source: Event detect when css property changed using Jquery
Without events, you can use setInterval function, like this:
var maxTime = 5000, // 5 seconds
startTime = Date.now();
var interval = setInterval(function () {
if ($('#element').is(':visible')) {
// visible, do something
clearInterval(interval);
} else {
// still hidden
if (Date.now() - startTime > maxTime) {
// hidden even after 'maxTime'. stop checking.
clearInterval(interval);
}
}
},
100 // 0.1 second (wait time between checks)
);
Note that using setInterval this way, for keeping a watch, may affect your page's performance.
7th July 2018:
Since this answer is getting some visibility and up-votes recently, here is additional update on detecting css changes:
Mutation Events have been now replaced by the more performance friendly Mutation Observer.
The MutationObserver interface provides the ability to watch for changes being made to the DOM tree. It is designed as a replacement for the older Mutation Events feature which was part of the DOM3 Events specification.
Refer: https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
(function() {
var ev = new $.Event('display'),
orig = $.fn.css;
$.fn.css = function() {
orig.apply(this, arguments);
$(this).trigger(ev);
}
})();
$('#element').bind('display', function(e) {
alert("display has changed to :" + $(this).attr('style') );
});
$('#element').css("display", "none")// i change the style in this line !!
$('#element').css("display", "block")// i change the style in this line !!
http://fiddle.jshell.net/prollygeek/gM8J2/3/
changes will be alerted.
Tried this on firefox, works http://jsfiddle.net/Tm26Q/1/
$(function(){
/** Just to mimic a blinking box on the page**/
setInterval(function(){$("div#box").hide();},2001);
setInterval(function(){$("div#box").show();},1000);
/**/
});
$("div#box").on("DOMAttrModified",
function(){if($(this).is(":visible"))console.log("visible");});
UPDATE
Currently the Mutation Events (like DOMAttrModified used in the
solution) are replaced by MutationObserver, You can use that to
detect DOM node changes like in the above case.
I just Improved ProllyGeek`s answer
Someone may find it useful.
you can access displayChanged(event, state) event when .show(), .hide() or .toggle() is called on element
(function() {
var eventDisplay = new $.Event('displayChanged'),
origShow = $.fn.show,
origHide = $.fn.hide;
//
$.fn.show = function() {
origShow.apply(this, arguments);
$(this).trigger(eventDisplay,['show']);
};
//
$.fn.hide = function() {
origHide.apply(this, arguments);
$(this).trigger(eventDisplay,['hide']);
};
//
})();
$('#header').on('displayChanged', function(e,state) {
console.log(state);
});
$('#header').toggle(); // .show() .hide() supported
A catch-all jQuery custom event based on an extension of it's core methods like it was proposed by different people in this thread:
(function() {
var ev = new $.Event('event.css.jquery'),
css = $.fn.css,
show = $.fn.show,
hide = $.fn.hide;
// extends css()
$.fn.css = function() {
css.apply(this, arguments);
$(this).trigger(ev);
};
// extends show()
$.fn.show = function() {
show.apply(this, arguments);
$(this).trigger(ev);
};
// extends hide()
$.fn.hide = function() {
hide.apply(this, arguments);
$(this).trigger(ev);
};
})();
An external library then, uses sth like $('selector').css('property', value).
As we don't want to alter the library's code but we DO want to extend it's behavior we do sth like:
$('#element').on('event.css.jquery', function(e) {
// ...more code here...
});
Example: user clicks on a panel that is built by a library. The library shows/hides elements based on user interaction. We want to add a sensor that shows that sth has been hidden/shown because of that interaction and should be called after the library's function.
Another example: jsfiddle.
I like plugin https://github.com/hazzik/livequery It works without timers!
Simple usage
$('.some:visible').livequery( function(){ ... } );
But you need to fix a mistake. Replace line
$jQlq.registerPlugin('append', 'prepend', 'after', 'before', 'wrap', 'attr', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove', 'html', 'prop', 'removeProp');
to
$jQlq.registerPlugin('show', 'append', 'prepend', 'after', 'before', 'wrap', 'attr', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove', 'html', 'prop', 'removeProp');

Waiting for model's data to be loaded using jQuery when -- experiencing timing differences?

I have an MVC application. I am trying to load a model from the server using jQuery's load. This works perfectly fine. I am now trying to run some JavaScript after all of my views have been loaded. As such, I am introducing jQuery's deferred promise functionality through use of jQuery .when
My limited understanding of this functionality has lead me to believe that the two bits of code below should run identically. It seems to me that my 'then' method is executing too soon, though. I'm not sure how to confirm that.
Old Code (Works):
$('#OrderDetails').load('../../csweb/Orders/OrderDetails', function () {
$("fieldset legend").off('click').click(function () {
var fieldset = $(this).parent();
var isWrappedInDiv = $(fieldset.children()[0]).is('div');
if (isWrappedInDiv) {
fieldset.find("div").slideToggle();
} else {
fieldset.wrapInner("<div>");
$(this).appendTo($(this).parent().parent());
fieldset.find("div").slideToggle();
}
});
});
Now, I would like to extend that to wait for multiple load events. To keep things simple, though, I am just going to try and wait for OrderDetails:
New Code (Doesn't Work):
var onDisplayLoadSuccess = function () {
console.log("Done!");
console.log('Fieldset legend:', $('fieldset legend'); //Not all found.
$("fieldset legend").off('click').click(function () {
var fieldset = $(this).parent();
var isWrappedInDiv = $(fieldset.children()[0]).is('div');
if (isWrappedInDiv) {
fieldset.find("div").slideToggle();
} else {
fieldset.wrapInner("<div>");
$(this).appendTo($(this).parent().parent());
fieldset.find("div").slideToggle();
}
});
};
var onDisplayLoadFailure = function () {console.error("Error.");};
$.when($('#OrderDetails').load('../../csweb/Orders/OrderDetails')).then(onDisplayLoadSuccess, onDisplayLoadFailure);
I do not see any errors fire. I see 'Done' print to the console, but the timing seems to be different. Legends which existed on the page prior to calling when/load/then have the click event applied to them, but legends which are loaded from in the view given back by OrderDetails do not have the click event bound to them.
By contrast, the old code's success function applied the click event to all legends appropriately. Why would this be the case?
To capture events on DOM elements that are added dynamically after binding an Event, you need to delegate it (http://api.jquery.com/on/).
Something like:
$('fieldset').on('click', 'legend', function(){

Unable to display a persistent load indicator while my time-taking plugin code is executing

I wrote a jquery plugin which filters the given dropdown's options as per the inputText specified and sorts them by number of words matched, with highest matches at the top. And this takes some time to execute (probably needs some study of the code to find if any optimization is possible). There is no ajax call inside my plugin code, pure client side logic. So, right now I need to display a load indicator. My problem is that I am not able to properly display a load indicator which persists while the filtration/sorting code executes.
Please note - My question here is not how to optimize the code but to display a persisting load indicator.
Here was my initial plugin code architecture -
(function($) {
// jQuery plugin definition
$.fn.filterGroup = function(config)
{
var settings = {
//some settings defined here
};
var thisDropdown = this; //master copy
if(config)
{
$.extend(settings, config);
}
this.each(function()
{
thisDropdown = filterGroupOptions(filtrableDropdown,inputText,settings,ignoreText);
});
// allow jQuery chaining
return thisDropdown;
};
function filterGroupOptions(filtrableDropdown,inputText,settings,ignoreText)
{
//here is my main plugin code which filters the given dropdown options as per the inputText specified, and this takes some time to execute
}
Now, because of the time taken by the filtration/sorting code, I need to display a load indicator while the filtration logic takes place, but I am so far not able to do that. At first I added a displayLoadIndicator() in the beginning of the code inside the this.each(function(){} and a hideLoadIndicator() at the end of the code insde the same this.each(function(){}. By displaying alerts at those points, I could see the the load indicator displayed and removed. I observed that even when the browser was hanging sometimes (with too many words entered for search term (inputText) was displaying Not Responding). So, I guessed may be I needed to add a callback to my code and call hideLoadIndicator() when I receive the callback, but still the load indicator does not persist. (updated plugin code)-
(function($) {
// jQuery plugin definition
$.fn.filterGroup = function(config)
{
var settings = {
//some settings defined here
};
var thisDropdown = this; //master copy
if(config)
{
$.extend(settings, config);
}
this.each(function()
{
showLoadIndicator();
//see I am calling hideLoadIndicator(); on receiving callback here
filterGroupOptions(filtrableDropdown,inputText,settings,ignoreText,function(returnVal)
{
thisDropdown = returnVal;
hideLoadIndicator();
// allow jQuery chaining
return thisDropdown;
});
});
};
function filterGroupOptions(filtrableDropdown,inputText,settings,ignoreText,callback)
{
//here is my main plugin code which filters the given dropdown options as per the inputText specified, and this takes some time to execute
callback(filtrableDropdown);
}
Previously, I added I added callback like this when there was an AJAX call whose response was taking time. But, never faced an issue like this where pure client side code is taking time...
Any pointers will be appreciated...
Updates
If possible at least describe the reason behind the phenomenon I am observing. Strange but I have to ask does adding a callback here makes sense?
Thanks
Try to add timeouts in your parts of the code to see the load indicator. This happens because JavaScript execution is faster than dom rendering so before we see anything the execution is complete and hide method hides the loading indicator
Try this:
(function($) {
// jQuery plugin definition
$.fn.filterGroup = function(config)
{
var settings = {
//some settings defined here
};
var thisDropdown = this; //master copy
if(config)
{
$.extend(settings, config);
}
showLoadIndicator();
setTimeout($.proxy(function(){
this.each(function()
{
//see I am calling hideLoadIndicator(); on receiving callback here
filterGroupOptions(filtrableDropdown,inputText,settings,ignoreText,function(returnVal)
{
thisDropdown = returnVal;
hideLoadIndicator();
// allow jQuery chaining
return thisDropdown;
});
//here is my main plugin code which filters the given dropdown options as per the inputText specified, and this takes some time to execute
});
}, this), 100);
};

jQuery seems to be running multiple calls simultaneously

Sorry, but apparently I don't understand chaining enough to figure out this problem...
I'm implementing a jQuery carousel plugin (jCarouselLite) and I'm trying to add an option to 'remove' one of the carousel items (currently <div class="remove">)...
initEvents: function() {
var self = this;
// Bind jQuery event for REMOVE button click
$('.remove').live('click', function() {
// Need to save the ID that we're removing
var item = $(this).closest('li.sort');
var itemId = item.attr("id");
$(this).removeItem();
self.refreshDisplay(itemId);
});
$.fn.removeItem = (function() {
var item = this.closest('li.sort'); // could save this call by passing param
item.fadeOut("normal", function() {
$(this).remove();
});
// preserve jQuery chain
return this;
});
},
refreshDisplay(itemId) {
// ...
// redraws carousel
// ...
// itemId is used to remove from the array of Items to show (class-wide scope)
}
Since there's no clean way to 'refresh' the jCarouselLite plugin (maybe something I'll try implementing in the actual plugin later) the quick and dirty fix for this is to just regenerate the Carousel.
The issue is I'm trying to fade out the element clicked, however, it seems like the refreshDisplay() is called before the animation of fading out (and removing) the clicked item is completed. I've verified this by commenting out the self.refreshDisplay(itemId); line and it fades out and removes as expected.
So I guess there's a certain way I need to chain this? I've done a couple hours of reading on how chaining works and I thought I understood it, but apparently not.
Any and all help is appreciated, thanks!
The purpose of chaining is to allow multiple commands to share a base object, but it doesn't cause each command to wait for the previous one.
For that, you need to use a callback. Something like
initEvents: function() {
var self = this;
// Bind jQuery event for REMOVE button click
$('.remove').live('click', function() {
// Need to save the ID that we're removing
var item = $(this).closest('li.sort');
var itemId = item.attr("id");
$(this).removeItem(function() {
self.refreshDisplay(itemId);
});
});
$.fn.removeItem = (function(callback) {
var item = this.closest('li.sort'); // could save this call by passing param
item.fadeOut("normal", function() {
$(this).remove();
callback(); //now your refresh is being called after the fade.
});
// preserve jQuery chain
return this;
});
},

Categories