I'm looking to build two functions:
var MyObject = new Object();
MyObject.MyProperty = 1;
function ListenToChange() {
here I want to listen to changes in MyProperty1 and do something when it changes
}
function ThrowEvent () {
here I change the value of MyProperty every 5 seconds
setTimeOut('ThrowEvent', 5000);
}
I looked at the addEventListener property but it looks like it works for DOM objects. I thought of using the ThrowEvent function to change the value of a hidden div and listen for the changes in the value of the hidden with $('#HiddenDiv').change() but I'm wondering if there's a better way to do it with the addEventListener.
Thanks for your help.
I can infer from your example you're using jQuery; you could trigger a custom event anytime you change the value of your property:
var my_obj = {my_prop: 1}
function ListenToChange(event, newval) {
console.log('my_prop is now' + newval)
}
$.bind("propchange:my_prop", ListenToChange)
function ThrowEvent () {
$.trigger('propchange:my_prop', my_obj.my_prop)
setTimeOut('ThrowEvent', 5000);
}
Well here is an example of what I came up with. It is not nearly as glamorous as John's example, but it gets the job done. Recommendations for improvements are more than welcome.
http://jsfiddle.net/yKYRs/
var MyObject = new Object();
MyObject.MyProperty = 1;
MyObject.MyProperty_Set = function(val) {
this.MyProperty = val;
ListenToChange();
}
function ListenToChange() {
console.log('My Property was changed');
}
MyObject.MyProperty_Set(2);
It seems that you are after ES5 set and get, however they won't work in an ECMAScript Ed 3 environment, you'll need to write your own getter and setter functions for that.
If you want to listen for changes to the DOM, there is a W3C DOM 2 Events specification that includes mutation events, however I don't think it was ever widely implemented. There is a level 3 working draft that deprecates a number of the level 2 features.
Related
I have a shared property across all the instances of my constructor:
function Me() { }
Me.prototype.window = {};
I'd like to update it's content on window resize, but I'd like to do it only once for each resize event, no matter how many instances of my constructor have been created.
Logically, if I define the eventListener as below, in the initialization of my instances, it will be fired multiple times
function Me() {
window.addEventListener('resize', function() {
this.window = window.outerWidth;
}.bind(this));
}
var a = new Me();
var b = new Me();
// on resize, the callback is called twice
How can I do it?
How can I do it?
Have a flag that indicates whether to bind the event handler or not. Then, in the constructor you only need to check the flag:
if (bindHandler) {
// ... bind handler
bindHandler = false;
}
How / where you store the flag is up to you.
Thought I'd put back all the answers I gave to show how OP has not provided all pertinent information up front. Also, the answer he finally came up with and marked correct was one I offered and he shot down.
First, I offered what is probably the most simple solution:
Put the following completely outside of the constructor function. When the window is resized, the shared property is updated - - for all instances.
window.addEventListener('resize', function() {
Me.prototype.window = window.outerWidth;
};
The OP then added new information that this would not be good because if no instances of Me existed, the callback would still be registered on the prototype.
I then offered this which interestingly, he marked as the answer when someone else posted it.
Your next solution would have to be to track whether there are any instances of Me before the event listener is registered. That would mean that you'd need to keep track of whether any instances exist:
// Global variabel to track if Me instances exist
var meInstances = false
var me1 = new Me();
meInstances = true;
if(meInstances){
window.addEventListener('resize', function() {
Me.prototype.window = window.outerWidth;
};
}
But, when I posted it, the response was: "you are completely making useless all the classes, constructors and stuff like that logic. There's not isolation, you are adding a lot of code for nothing and the solution is not robust." In fact, the OP then came up with his own solution that uses an array to store the instances and then the length of the array can be checked to see if there are any. I was actually going to suggest that, but went with the Boolean flag because the OP kept saying he wanted simple.
So, I offered this:
What about:
function Me() {
// This code only runs when instance is being made
// This instance is registers a callback
window.addEventListener('resize', function() {
// Callback modifies single Prototype that all instances share
Me.prototype.window = window.outerWidth;
});
}
Me.prototype.window = {};
Now, I don't fully endorse this as a good solution, but from all the constraints the OP kept adding after each suggestion, this seemed to be a last resort. But, again was rejected.
Adding here the solution I've used in the final code.
I've added a destroy method for completeness:
function Me() {
this.init();
}
Me.prototype.window = 0;
// function used to update this.window
Me.prototype.onResizeBound = null;
Me.prototype.onResize = function() {
Me.prototype.window = window.outerWidth;
};
// if there are no instances of Me, initialize the event listener
// then, add this instance to the instances list
Me.prototype.init = function() {
if (this.instances === 0) {
Me.prototype.onResizeBound = this.onResize.bind(this);
window.addEventListener('resize', Me.prototype.onResizeBound);
}
this.instances++;
};
// remove this instance to the instances list
// if there are no more instances in the list, remove the event listener
Me.prototype.destroy = function() {
this.instances--;
if (this.instances === 0) {
window.removeEventListener('resize', Me.prototype.onResizeBound);
}
};
Me.prototype.instances = 0;
Example:
var a = new Me(); // added event listener
var b = new Me(); // not added event listener since one is already set
b.destroy(); // not removed event listener since one instance is still there
a.destroy(); // removed event listener since there are not other instances
Is there a way in Javascript to have a delegate like the ones in c# ?
Example in c#
Object.onFunctionCall = delegate (vars v) {
Console.WriteLine("I can do something in this private delegate function");
};
I would like with my Javascript to have my main object do something over a long time, and shot a delegate once in a while to give a little update. All that without having to change the code itself of my class to adjust for the webpage.
function mainObject() {
this.onUpdate = function() { //Potentially the delegate function here
}
}
var a = new mainObject();
a.onUpdate = Delegate {
$(".myText").text("Just got a delegate update");
}
I dunno if it's clear enough.. havent found ressources on this so I suppose there is just no way to do so ?
NOTE: I am not looking into jquery Click delegates event here, but into delegating a function call like how it works in c#
Let me know
Although the original question was ansered by solving the root problem (observer - pattern) there is a way to implement delegates in JavaScript.
The C# delegate pattern is available in native JavaScript using context binding. Context binding in JavaScript is done with the .call method. The function will be called in the context given by the first argument.
Example:
function calledFunc() {
console.log(this.someProp);
}
var myObject = {
someProp : 42,
doSomething : function() {
calledFunc.call(this);
}
}
myObject.doSomething();
// will write 42 to console;
What you are looking for is an "Observer Pattern", as described eg. here.
But as you are interested in jQuery, you don't need to go the trouble of writing an observer pattern for yourself. jQuery already implements an observer in the guise of its .on() method, which can be invoked on a jQuery collection to cause callback function(s) to fire every time a native or custom event is dispatched.
Here's an example :
$(function() {
//attach a custom event handler to the document
$(document).on('valueChange', function (evt) {
$(this).find("#s0").text(evt.type);
$(this).find("#s1").text(evt.value);
$(this).find("#s2").text(evt.change);
$(this).find("#s3").text(evt.timestamp).toLocaleString();
});
//customEvent(): a utility function that returns a jQuery Event, with custom type and data properties
//This is necessary for the dispatch an event with data
function customEvent(type, data) {
return $.extend($.Event(type||''), data||{});
};
//randomUpdate(): fetches data and broadcasts it in the form of a 'changeValue' custom event
//(for demo purposes, the data is randomly generated)
function randomUpdate() {
var event = customEvent('valueChange', {
value: (10 + Math.random() * 20).toFixed(2),
change: (-3 + Math.random() * 6).toFixed(2),
timestamp: new Date()
});
$(document).trigger(event);//broadcast the event to the document
}
});
Here's a demo, complete with "start" and "stop" buttons for a regular "interval" dispatch of the custom event.
Notes
Under some circumstances, it might be more appropriate to broadcast the event to the four data spans individually.
On the web, you will find mention of a more convenient jQuery.event.trigger({...}) syntax. Unfortunately this was an undocumented feature of jQuery, which disappeared at v1.9 or thereabouts.
I'm writing an awesome IIFE and want this to be as easy as possible for my users who use it. So I was thinking since some of them don't know that to easily remove an eventlistener without it already being a function we can give that inline function a name
Example
document.addEventListener('click',function dood(){
//some function
},false);
document.removeEventListener('click',dood,false);
//instead of
function dood(){
//some function
}
document.addEventListener('click',dood,false);
document.removeEventListener('click',dood,false);
But since they shouldn't know the name exactly I was wondering if we could do
var k = "name_of_function";
document.addEventListener('click',function window[k](){
//the function
},false);
Though I know this does not work is there a way to do this? I'd like to make it so they can easily do this
object.cancel('name_of_function') //which will be the name of the instance
// they created earlier if they gave that instance a name
object={
cancel:function(nm){
document.removeEventListener(self.trigger,window[nm],false);
//self.trigger really is this.trigger which they assign as either scroll,click,mousemove,etc.
}
};
Any ideas? Or is this not possible at all?
usage is:
scrollex('element',{
max:500,
min:500,
pin:200,
offset:0.5,
name:'google',//this then would be used in multiple instances
});
scrollex.events //shows all events and their names
scrollex.listen('google'); //it'll console log all info for this event
scrollex.cancel('google');
I think you're on the right track. But you should not use window, and some local object instead. And dynamically naming function expressions (or whatever that function window[k](){} was supposed to mean) is impossible a pain - don't try this. Just let them stay anonymous, and reference them only via property names / variables.
var object = (function() {
var listeners = {
name_of_function: function dood(){…}
};
document.addEventListener('click', listeners.name_of_function, false);
return {
cancel: function(nm) {
document.removeEventListener('click', listeners[nm], false);
}
};
}());
// now, you can
object.cancel('name_of_function')
I'm using JS and jQuery for the first time after a lot of experience with Java and C++. I'm loving jQuery's idea of $(document).on('click', 'btn-selector', react), but for more complex widgets I'm finding myself in the same rut over and over: in each react handler, I have to look up the widget as a whole and reconstruct all my knowledge about it.
For example, I'm making a simple widget out of <input>s with which the user can make a grading scale: 90 maps to an A, 80 maps to a B, etc. When one of the inputs changes, I want to check to make sure that the inputs are still in order (your scale can't go 90, 70, 80, for example).
So, I have something like
Actual
$(document).on('click', '.scale-input', function() {
var widget = $(this).closest('.scale-widget-container');
ensureLevelsAreInOrder(widget);
});
Almost every single handler has to have this first line to find its context. I'd much rather have code that looks like this:
Preferred
$(document).on('click', '.scale-input', ensureLevelsAreInOrder);
The problem is that in this form, ensureLevelsAreInOrder only has a reference to the input that changed, not the larger context.
In Java or C++, I would have called a constructor on the widget, and each input would have a handler with the context baked in via member variables. I could do something similar with
$(function() {
$('.scale-widget-container').scaleWidget();
});
with scaleWidget() setting up the contextualized handlers, but the page I'm working with loads a lot of its html with ajax and I don't have a reliable time to run that initialization.
Is this a common problem that we just have to deal with if we don't want JS in our HTML, or is there a solution I haven't come across yet?
Not sure what it is you're after exactly, but you don't seem to touch on two quite important concepts when it comes to JS: the event object, and closures. Both of these are open to you to get what you need:
event object:
The callback function is passed an argument, that describes the event itself, and references the elements affected by that event, This isn't exclusive to jQ (just google addEventListener), but it's quite handy:
$(document).on('click', '.scale-input', function(e)//<-- e is our event
{
console.log(e);//check console
});
Which, in vanilla JS would look like this:
document.addEventListener('click', function(e)
{
if (!e.className.test(/\bscale\-input\b/))
{
return e;
}
console.log(e);
}, false);
Another thing you might want to consider is enclosing references to whatever it is you need in an IIFE's scope:
(function()
{
var containers = $('.scale-widget-container'),
localBool = false,
asMany = 'varsAs you need',
previousScales = [],
inputs = $('.scale-input');//references to all DOM nodes you mention
$(document).on('click','.scale-input',function(e)
{
console.log($(this));
console.log(containers);
previousScales.push(this.value);//or something
console.log(previousScales);
//and so on.
});
}());
Hope this helped
Update:
If IE isn't a browser you don't care about that much, you could use one of the DOM-modified events, specifically DOMTreeModified:
(function()
{
var nodes = [];//<-- store current nodes here, if applicable
nodes.containsNode = function(node)
{
var i;
for (i=0;i<this.length;i++)
{
if (this[i] && this[i] === node)
{//node is set, return its index
return i;
}
}
//node not found, return -1
return -1;
};
document.body.addEventListener('DOMSubtreeModified',function(e)
{
var all = document.getElementsByClassName('scale-input'),
i;
for (i=0;i<all.length;i++)
{
if (nodes.containsNode(all[i]) === -1)
{
nodes.push(all[i]);//add new
}
}
},false);
}());
More on the mutation events, and their issues, on the DOM events wiki
In a Node.js-based project, I have a couple objects:
EmitterA
EmitterB
Consumer
In Consumer's constructor, I have the following:
var a = new EmitterA();
a.on('someEvent', someEventHandler);
In EmitterA's constructor, I have the following:
var self = this;
var b = new EmitterB();
b.on('someEventFromB', function() {
self.emit('someEvent');
});
Finally, in EmitterB's constructor, I have something like this:
this.emit('someEventFromB');
The problem is that Consumer's someEventHandler() doesn't get fired when the inital .emit() from EmitterB is fired. The reason is one of timing. Consumer doesn't subscribe to someEvent on EmitterA until after the event has been fired.
Is there a way to attach event handlers at the time of construction? I have considered utilizing the newListener event, but this will be problematic when working with multiple listeners later on, and situations where there are many different types of events that will be fired. I'd like to avoid this route if possible, and am looking for alternatives that you may suggest. Thanks!
try something like:
var a = new EmitterA({
events : {
someEvent : someEventHandler
}
});
function EmitterA( props ){
props || ( props = {} );
if( props.events ){
for( var n in props.events ){
this.on( n, props.events[n] );
}
}
// the rest of the constructor code
}
I just thought of an alternate way that works for my specific case, but may be problematic for others. Please use it with a grain of salt.
In EmitterB's constructor, I changed this.emit('someEventFromB'); to this:
process.nextTick(function() {
this.emit('someEventFromB');
}
See also: http://howtonode.org/understanding-process-next-tick