Javascript: Functions as properties - javascript

Say I had code like so:
function on(loc,type,func){
loc.addEventListener(type, function(e){
func(e);
});
}
If I called it like this:
on(document.getElementById('test'),"click",function(){
alert('You clicked!');
});
It would work, but I want to be able to call the on function like so:
document.getElementById('test').on('click', function(){
alert('You clicked!');
});
How can I make it able to be called like so?

As has been pointed out, the DocumentElement does not have an .on() method. Yet!
You can, however, add one by extending the prototype, adding a new property and making your function available on all Elements. This is quite easy (and I'll provide a trivial example in a moment), but it's also widely considered to be a bad practice. So before you try this out, understand that Javascript absolutely does make it possible...but that doesn't mean it's a good idea.
Now, that example:
Element.prototype.test = function() {
console.log("Successfully extended the prototype of a native object!");
}
document.getElementById("footer").test();

You cannot do this because the DocumentElement does not have a property (or allow you to create one (that I know of)) of your on function.
You could get away with doing something similar such as modifying your on function to handle the onlcick event for a given element. I haven't tested the below code but it should give you an idea:
function on (element) {
var obj = {
clickEvent : function () {
element.onclick = function () {
alert("you clicked");
}
}
};
return obj;
}
var theElement = document.getElementById("test");
on(theElement).clickEvent();

Related

Dynamically loaded onclick function using in function data

Ok, my coding is a little weak, but I'm learning. I have a code simular to
$.post("test.php", {"data":"data"}, function(grabbed){
$.each(grab, function(i, item){
var place = document.getElementById("div");
var para = createElement("p");
para.innerHTML = item.name;
para.onclick = function(){
document.getElementById(?para?).innerHTML = ?item.price?;
}
place.appendChild(para);
});
}, "json");
My question is how would I use say, item.price in the onclick function. I haven't actually tried yet, but I'm pretty sure that referencing the data retrieved from the request won't work in the dynamically loaded onclick function.
My function works a little differently but the basic principle is on here. Any advise would be appreciated, or perhaps someone can point me in the right direction.
I was thinking of assigning ids to the div or para and using those in the function somehow. I also thought of using cookies to store the data I want, but I can't figure it out. Is there something else I need to know or try.
Your question concerns the concept of closures in JavaScript.
Closures are functions that refer to independent (free) variables. In
other words, the function defined in the closure 'remembers' the
environment in which it was created.
Consider the following code snippet:
var foo = (function(){
var remembered = "yea!";
return function(){
console.log("Do you remember? - " + remembered);
};
})();
foo(); // prints: Do you remember? - yea!
That said, looking at your code:
...
// the function assigned to `onclick` should remember
// the variable `item` in the current context.
para.onclick = function(){
document.getElementById(?para?).innerHTML = ?item.price?;
}
...
That works since you're using JQuery's $.each() for iteration. And here is a demonstration: http://jsfiddle.net/55gjy3fj/
However if you were doing the iteration with a plain for-loop, you would have to wrap the handler inside another closure:
var getHandler = function(para, item){
return function(){
document.getElementById(the_id_here).innerHTML = item.price;
};
};
para.onclick = getHandler(para, item);
...
This fiddle demonstrates the point: http://jsfiddle.net/63gh1gof/
Here's a fiddle that summarises both cases:
Since you're already using jQuery, this could be the perfect opportunity to get introduced to .data(). Use it to save an arbitrary object to the dom element, and then reference it later in the onclick-listener.
var para = $('<p/>').html(item.name).data('item', item);
para.click(function() {
$(this).data('item') // item!!!
});
$('#div').append(para);
Actually, here you could write a more efficient method using jQuery's delegated event handlers.
Just attach the event handler to the parent element:
$('#div').on('click', 'p', function() {
$(this).data('item') // item!!!
});

How to pass a variable to jQuery's .fadeOut()

I have an object that I want to pass to .fadeOut().
Before we get to that, here's how I can pass an object to .click():
this.$title.click({story: this}, function (event){
var story = event.data.story;
}
Simple enough.
Now I want to do something similar with .fadeOut:
this.$title.fadeOut("fast", {story: this}, function(){
var story = ???
});
Which doesn't work. But you get the idea? How can I pass this into the anon function?
I'm looking for the cleanest solution. Barring that, I'm looking for the solution that's most in line with what I've done with .click().
Thanks!
ASIDE: is there a cleaner way pass this into .click()?
This is rather a question about JS than about jQuery; you can do it like that:
var story = this
this.$title.click(function () {
/* story still available */
})
this.$title.fadeOut('fast', function () {
/* same here */
})
Or something more fancy (this also preserves the content of story at the moment of assignment even if it gets overwritten in the upper scope later on):
this.$title.click((function (story) {
return function () {
/* story is available */
/* this will be passed to $(...).click(...) */
}
})(this))
It seems fadeOut() doesnt have an overload like click() where you can pass eventData
.click( [eventData ], handler )
Therefore, make a preserve this in a closure and use inside the function
var story = this;
this.$title.fadeOut("fast", function(){
//story can be used here
});

Variable as eventListener named function expression name

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')

What is the best practice to add functions to a function in JavaScript?

I have an occurence where I want to have a main js-file with one resize function and specific files that can add workload to the main file without changing the mainfile and manually calling functions.
Lets say I have an object literal
var App = {
resize: function(){
// Code should be executed here
},
addResize: function(){
// ?
}
}
and I want a function to add code to the resize function which dynamically adds workload to the resize function (which gets called on window resize):
App.addResize(function(){ ... });
The first thing that came to my mind is to store the anonymous functions from addResize to an array and iterating over it in the resize function, but that doesn't feel like doing a best-practice:
var App = {
resizeFunctions = [];
resize: function(){
// iterate over resizeFunctions and call each one
// here I define throttling/debouncing ONCE
},
addResize: function(fn){
this.resizeFunctions.push(fn);
}
}
window.onresize = App.resize();
App.addResize(fn1);
App.addResize(fn2);
Is there a better way?
as you are referring to one function, ie. a resize function, I assume that you are looking for function overloading:
Function overloading in Javascript - Best practices
http://ejohn.org/blog/javascript-method-overloading/
If you want to extend the functionality of a set of methods that are all related to a single parent-object into different child objects, I would look into prototypal inheritance.
It allows you to define re-define the parent methods for each of the child-objects.
Do you want to overwrite the existing function?
Then you can just do this:
App.addResize = function(){}
App.addResize(function(){ ... });
would pass the function to addResize as an attribute but not add it to it. You could do
App.addResize.newFunction = function(){ ... };
Where newFunction is the name of the function
You can treat your object literal as array.
App["resize"] = function(){
//Code goes here
}
__
Or
App.resize = function(){
//Code here
}
Both are equally good. These will update the definition of resize
If you want to add some new method, then too the same syntax will work.
App["myNewMethod"] = new function(){
//Code here
}
Edit after OP's comment
var oldFun = App["resize"]; // or may be store this in App itself
App["resize"] = function(){
//Pre-processing
// Now call original resize method
oldFun(); //If resize method used method argument then use oldFun.apply( this, arguments );
//Post processing
}

How to refer to object in JavaScript event handler?

Note: This question uses jQuery but the question has nothing to do with jQuery!
Okay so I have this object:
var box = new BigBox();
This object has a method named Serialize():
box.AddToPage();
Here is the method AddToPage():
function AddToPage()
{
$('#some_item').html("<div id='box' onclick='this.OnClick()'></div>");
}
The problem above is the this.OnClick() (which obviously does not work). I need the onclick handler to invoke a member of the BigBox class. How can I do this?
How can an object refer to itself in an event handler?
You should attach the handler using jQuery:
function AddToPage()
{
var self = this;
$('#some_item').empty().append(
$("<div id='box'></div>")
.click(function() { self.OnClick(someParameter); })
);
}
In order to force the event handler to be called on the context of your object (and to pass parameters), you need to add an anonymous function that calls the handler correctly. Otherwise, the this keyword in the handler will refer to the DOM element.
Don't add event handlers with inline code.
function AddToPage()
{
$('#some_item').html("<div id='box'></div>");
$('#box').click(this.OnClick);
}
EDIT:
Another way (avoids the extra select):
function AddToPage()
{
var div = $('<div id="box"></div>'); // probably don't need ID anymore..
div.click(this.OnClick);
$('#some_item').append(div);
}
EDIT (in response to "how to pass parameters");
I'm not sure what params you want to pass, but..
function AddToPage()
{
var self = this, div = $('<div></div>');
div.click(function (eventObj) {
self.OnClick(eventObj, your, params, here);
});
$('#some_item').append(div);
}
In jQuery 1.4 you could use a proxy.
BigBox.prototype.AddToPage= function () {
var div= $('<div>', {id: box});
div.click(jQuery.proxy(this, 'OnClick');
div.appendTo('#some_item');
}
You can also use a manual closure:
var that= this;
div.click(function(event) { that.OnClick(event); });
Or, most simply of all, but requiring some help to implement in browsers that don't yet support it (it's an ECMAScript Fifth Edition feature):
div.click(this.OnClick.bind(this));
If you are using jQuery, then you can separate your code from your markup (the old seperation of concerns thing) like this
$(document).ready(function() {
var box = new BigBox();
$('#box').click(function() {
box.serialize();
});
});
You only need to add the click handler once for all divs with id of box. And because the click is an anonymous function, it gets the scope of the function it is placed in and therefore access to the box instance.

Categories