Say I have this structure:
File1.js:
var myObject = {
bindEvents: function() {
console.log('Root events binding');
}
}
keyboard-object.js:
myObject.bindEvents: function() {
console.log('Keyboard events binding');
}
mouse-object.js:
myObject.bindEvents: function() {
// extends original bindEvents and adds more functionality
// right now this behavior overrides the original bindEvents method
console.log('Mouse events binding');
}
How can I trigger myObject.bindEvents() and make sure it is fired in each file?
My purpose is to split one big object into separate files and make one method that fires all the corresponding method(s) in each file, as in bindEvents should trigger bindEvents (or keyboardEvents in my case) in the object
You are essentially after a custom events handler for objects here. I would take a look at the one that Backbone.js supplies.
Be ware that you are not assigning an anonymous function to (earlier declared) myObject properties with the described syntax (as in):
myObject.bindEvents: function() {
console.log('Keyboard events binding');
}
What you are doing here is actually labeling the anonymous function with the word "myObject.bindEvents" which doesn't do you any good.
I think that - aside from this error - you are trying to do something like this:
myObject.bindEvents = function() {
console.log('Keyboard events binding');
}
And only Now your myObject has a property method of bindEvents which than you can invoke by simply declaring:
myObject.bindEvents(); later on your script.
Ok eventually I decided to use two (or more) seperate objects and trigger methods by firing events which are set on the document.
So my current solution is this:
myObject.js
var myObject = {
bindEvents: function() {
// some code here
$(document).trigger('myObject:bindEvents');
}
}
secondObject.js
var secondObject = {
bindEvents: function() {
// separate code and logic here
}
}
$(document).on('myObject:bindEvents',function(){
secondObject.bindEvents();
});
This gives me the flexibility to add more separate objects and bind their methods to events which are fired by the myObject object.
I'm working on some legacy code and have come across 3 similar constructs for triggering events
command/comply
trigger: .command('update:mySetting', newSetting);
handle: .comply('update:mySetting', myCallback);
listento/trigger
trigger: .trigger('change');
handle: .listenTo(myModel, 'change', myCallback);
request/reply
trigger: .request('change');
handle: .reply('change', myCallback);
What is the difference between these events and when should I use each of them?
Thanks
Note: I'm not sure if all of them come from marionette
https://github.com/marionettejs/backbone.radio
reply is used when you need to return a value when a request is made.
For example
Radio.channel('global').reply('something', function() { return 'something';});
// can also be
// Radio.channel('global').reply('something', 'something');
//... somewhere else in the code base
// someValue = 'something'
var someValue = Radio.channel('global').request('something');
You don't have to return anything with request/reply and just use it as a way to run a function. Doing this will make it work like command/comply, which makes command/comply not needed.
You can have one reply for a request, so redefining a reply will overwrite the last definition. It's one to one, for a reply you have a corresponding request.
// before
Radio.channel('global').reply('something', function() { return 'something';});
// somewhere else, it gets changed
Radio.channel('global').reply('something', 'not something');
Make changes at your discretion.
trigger/listenTo is the typical event system.
trigger can emit an event from anywhere in the code.
listenTo allows many listeners to listen to that event to do what is needed when fired.
Radio.channel('global').trigger('myEvent');
// somewhere in the code
view1.listenTo(Radio.channel('global'), 'myEvent', function() {
// do something
});
// somewhere else in the code
view2.listenTo(Radio.channel('global'), 'myEvent', function() {
// also do something
});
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');
Well, this is not my first jQuery plugin but the first I think others would benefit from. So for me its the first time to make sure every possible application works as predicted.
My plugin handles modal less overlays and therefore has to listen for some events. If the plugin is re-initialized the old listeners seem to be still in place and cause malfunction.
My solution to this looks like this:
var oldSettings = $(_this).data('mlOverlaySettings');
if(oldSettings) {
$(oldSettings.target).unbind('click.mlOverlay');
$(document).unbind('click.mlOverlay');
$(document).unbind('keyup.mlOverlay');
}
For me it seems like the problem has anything todo with saving the plugin state with the $.data function and not properly released references.
$(_this).data('mlOverlaySettings', settings);
$(_this).data('mlIsOverlayVisible', false);
Additional resources
Plugin Demo
Plugin documentation and full source code
Unbinding the old event's is somewhat ugly. Am I doing something wrong here or is this always required?
You should prevent reinitialization in the jQuery boilerplate method:
(function($) {
var PLUGIN_IDENTIFIER = "my-plugin";
...plugin definition etc
//jQuery boilerplate
$.fn.myPlugin = function(opts) {
return this.each(function() {
var instance = $(this).data(PLUGIN_IDENTIFIER);
//Prevent reinit on this element
if (!instance) {
instance = new MyPlugin(this, opts);
$(this).data(PLUGIN_IDENTIFIER, instance);
}
//Method call
if (typeof opts === "string") {
instance[opts].apply(instance, [].slice.call(arguments, 1));
}
});
};
})();
You should always provide a "destroy" method that removes the .data and event listeners it added too. So it's only possible to reinitialize after calling "destroy" which conveniently removed the event listeners too.
Here's an example implementation of a pretty standard destroy method:
function MyPlugin(element, opts) {
this.element = $(element);
this.opts = $.extend(defaults, $(element).data(), opts);
//Other instance state
//absolutely do not use $.data for this, you should only occupy one $.data
//slot for your plugin for the same reason you only occupy one slot on
//$.fn
}
MyPlugin.prototype.destroy = function() {
this.element.removeData(PLUGIN_IDENTIFIER);
this.element.off(".myplugin"); //Remove all events off the element that belong to the plugin's namespace
//.remove() any helper elements created by the plugin
this.element = null;
};
I have a jQuery plugin that needs to register a click event handler:
$.fn.myPlugin = function (options) {
var settings = {
// snipped
};
$.extend(settings, options || {});
$("body").click(function () {
// Do Something
});
// Rest of the plugin
});
The problem is that multiple invocations register the function more than once. Since the function needs to stay attached, I can't use .one().
Is there a way if a function is already attached? Can I give it a name or so? Or do I have to set some boolean flag using closure magic?
Namespace your events.
$('body').unbind('click.myPlugin').bind('click.myPlugin', function() {
..code...
});
More on Namespaced Events.
A very easy method with good performance would be to set a data element on the body element:
if (!$.data(document.body, 'myPluginRegistered') {
$.data(document.body, 'myPluginRegistered', true);
// do your plugin code here
}
Easiest might be the boolean plus closure magic you suggested. Alternatively, you could get the list of all functions bound to "click" object, and see if the function you're attaching is there already.
This question shows how to get the list.
List all javascript events wired up on a page using jquery
Though, the namespace suggestion that came in after I first responded is probably simpler.