How to dynamically assign DOM event and then execute it? - javascript

i am trying to execute DOM events within a function that expects the event to handle, such as onmouseover, onclick and so on, by name as a function parameter like so:
...
doSomething(target, 'onmouseover');
doSomething : function(tgt, evt)
{
...
o.evt = function() {
alert(evt);
}
...
}
...
The assignment does not throw any errors, so i guess, it is syntactically correct, but it does also not do the alert. Why?
(Please do not recommend using frameworks. I would like to understand my mistake and how to get this managed.)

You can assign the event dynamically using the [] notation:
Demo
var obj = {
doSomething : function(tgt, evt)
{
tgt[evt] = function() {
alert(evt);
}
}
};
obj.doSomething(document.getElementById("test"), 'onmouseover');
​

Related

Uncaught TypeError: object is not a function (calling onchange function)

My code is big so I will keep it short. I am tring to fire onchange event of Select atribute. Problem is that when I fire it I got this error:
Uncaught TypeError: object is not a function
Since my code is big I will show to you codes where I decalare onchange event and when I fire it.
Here is when onchange function is declared:
function e(b, c) {
function a() {
if (b.current_modal) {
b.current_modal.className = "hide"
}
f("overlay").className = "hide"
}
jigsaw.UI.close_lightbox = a;
Util.$("overlay").click(a);
Util.$("set-parts").change(function () {
if (this.parts != 9) {
jsaw.set_image("images/puzzle.jpeg")
} else {
jsaw.set_image("images/puzzle1.jpeg")
}
c.emit(jigsaw.Events.PARTS_NUMBER_CHANGED, +this.value);
c.emit(jigsaw.Events.RENDER_REQUEST)
});
Util.$("game-options").click("a", function (h) {
if (jigsaw.Events[this.id]) {
h.preventDefault();
c.emit(jigsaw.Events[this.id])
}
})
}
and here is when I fire onchange event:
show_time: function () {
/*.....*/
javascript: document.getElementById("date").innerHTML = ScoreResult;
document.getElementById("set-parts").selectedIndex = 1;
document.getElementById('set-parts').onchange();
}
I don't use jquery, but in JavaScript there is a difference on how addEventListener(eventName, ...) and on*eventName* = function(){} works.
I suppose that not modern library will use on*eventName* interface for one simple reason: I can hold just a single listener. But the on*eventName* are still there to support legacy code.
Now, in your particular case, when you add the event your library is using the addEventListener/removeEventListener/dispatchEvent underline DOM interface. This will not set up the legacy on*eventName* property to your callback, so the only way you can directly call it it by dispatching an event with the DOM method dispatchEvent (or your library wrapper) with an Event object with type 'change'. That is what "new Event('change')" does. If you need to pass extra properties to the event you can create it like this:
new Event('change', { foo : 1, bar : 'hello, world!' })
and those properties will be available in your listener callback arguments[0] under whichever name you set your argument (in your case, you left it empty).

How to detect when an .html() function is called in jQuery?

The problem is simple. I have a massive javascript application. And there are lot of times in the app where I use code which looks something like this -
$('#treat').html(new_data);
....
....
$('#cool').html(some_html_data);
....
....
$('#not_cool').html(ajax_data);
So what I want to do is, everytime this html() function is called I want to execute a set of functions.
function do_these_things_every_time_data_is_loaded_into_a_div()
{
$('select').customSelect();
$('input').changeStyle();
etc.
}
How do I do this? Thank you.
You can use the custom event handlers for that:
$('#treat').html(new_data);
// Trigger the custom event after html change
$('#treat').trigger('custom');
// Custom event handler
$('#treat').on('custom', function( event) {
// do_these_things_every_time_data_is_loaded_into_a_div
alert('Html had changed!');
});
UPDATE
Based on answer over here and with some modifications you can do this:
// create a reference to the old `.html()` function
$.fn.htmlOriginal = $.fn.html;
// redefine the `.html()` function to accept a callback
$.fn.html = function (html, callback) {
// run the old `.html()` function with the first parameter
this.htmlOriginal(html);
// run the callback (if it is defined)
if (typeof callback == "function") {
callback();
}
}
$("#treat").html(new_data, function () {
do_these_things_every_time_data_is_loaded_into_a_div();
});
$("#cool").html(new_data, function () {
do_these_things_every_time_data_is_loaded_into_a_div();
});
Easily maintainable and less code as per your requirements.
You can overwrite the jQuery.fn.html() method, as described in Override jQuery functions
For example, use this:
var oHtml = jQuery.fn.html;
jQuery.fn.html = function(value) {
if(typeof value !== "undefined")
{
jQuery('select').customSelect();
jQuery('input').changeStyle();
}
// Now go back to jQuery's original html()
return oHtml.apply(this, value);
};
When html() is called it usually make the DOM object changes, so you can look for DOM change event handler, it is called whenever your HTML of main page change. I found
Is there a JavaScript/jQuery DOM change listener?
if this help your cause.
You can replace the html function with your own function and then call the function html:
$.fn.html = (function(oldHtml) {
var _oldHtml = oldHtml;
return function(param) {
// your code
alert(param);
return _oldHtml.apply(this, [param]);
};
})($.fn.html);
I have a little script for you. Insert that into your javascript:
//#Author Karl-André Gagnon
$.hook = function(){
$.each(arguments, function(){
var fn = this
if(!$.fn['hooked'+fn]){
$.fn['hooked'+fn] = $.fn[fn];
$.fn[fn] = function(){
var r = $.fn['hooked'+fn].apply(this, arguments);
$(this).trigger(fn, arguments);
return r
}
}
})
}
This allow you to "hook" jQuery function and trigger an event when you call it.
Here how you use it, you first bind the function you want to trigger. In your case, it will be .html():
$.hook('html');
Then you add an event listener with .on. It there is no dynamicly added element, you can use direct binding, else, delegated evets work :
$(document).on('html', '#threat, #cool, #not_cool',function(){
alert('B');
})
The function will launch everytime #threat, #cool or #not_cool are calling .html.
The $.hook plugin is not fully texted, some bug may be here but for your HTML, it work.
Example : http://jsfiddle.net/5svVQ/

How to handle events in jQuery UI widgets

I'm trying to write a jQuery widget following the model given here.
Here is a snapshot of the widget:
(function ($) {
$.widget("ui.notification", {
_create: function () {
if (!this.element.hasClass("ntfn")) {
this.element.addClass("ntfn");
}
this.elTitle = this.element.append("<div class='ntfn-title'>Notifications</div>");
this.elTitle.click(this._titleClick)
},
_titleClick: function () {
console.log(this);
}
});
})(jQuery);
Here the problem is with the scope of "this" inside the _titleClick method, inside the method this points to the title element. But I need it to point to the widget element.
I think one way of doing it will be to use a wrapper class like
var that = this;
this.elTitle.click(function() {
that._titleClick.apply(that, arguments);
});
Is this the best way to solve this problem or is there any general pattern to solve this issue?
Use the this._on() method to bind the handler. This method is provided by the jQuery UI widget factory and will make sure that within the handler function, this always refers to the widget instance.
_create: function () {
...
this._on(this.elTitle, {
click: "_titleClick" // Note: function name must be passed as a string!
});
},
_titleClick: function (event) {
console.log(this); // 'this' is now the widget instance.
},
You should look to jQuery.proxy() http://api.jquery.com/jQuery.proxy/
el.bind('evenname', $.proxy(function () {
this.isMyScope.doSomething();
}, scope));
I wrote a method my own to solve this issue
_wrapCallback : function(callback) {
var scope = this;
return function(eventObject) {
callback.call(scope, this, eventObject);
};
}
In your create, init (or somewhere in your instance) function do this:
_create: function() {
...
// Add events, you will notice a call to $.proxy in here. Without this, when using the 'this'
// property in the callback we will get the object clicked, e.g the tag holding the buttons image
// rather than this widgets class instance, the $.proxy call says, use this objects context for the the 'this'
// pointer in the event. Makes it super easy to call methods on this widget after the call.
$('#some_tag_reference').click($.proxy(this._myevent, this));
...
},
Now define your objects event hander like this:
_myevent: function(event) {
// use the this ptr to access the instance of your widget
this.options.whatever;
},
define var scope=this, and use scope in event handler.
_create: function () {
var scope = this;
$(".btn-toggle", this.element).click(function () {
var panel = $(this).closest(".panel");
$(this).toggleClass("collapsed");
var collapsed = $(this).is(".collapsed");
scope.showBrief(collapsed);
});
},
Another way to do the same thing without using closure, is to pass the widget as a part of the event data like so:
// using click in jQuery version 1.4.3+.
var eventData = { 'widget': this };
// this will attach a data object to the event,
// which is passed as the first param to the callback.
this.elTitle.click(eventData, this._titleClick);
// Then in your click function, you can retrieve it like so:
_titleClick: function (evt) {
// This will still equal the element.
console.log(this);
// But this will be the widget instance.
console.log(evt.data.widget);
};
It used to be via the jquery bind method now on is favoured.
As of jQuery 1.7, the .on() method is the preferred method for
attaching event handlers to a document. For earlier versions, the
.bind() method is used for attaching an event handler directly to
elements. Handlers are attached to the currently selected elements in
the jQuery object, so those elements must exist at the point the call
to .bind() occurs. For more flexible event binding, see the discussion
of event delegation in .on() or .delegate().
_create: function () {
var that = this;
...
elTitle.on("click", function (event) {
event.widget = that; // dynamically assign a ref (not necessary)
that._titleClick(event);
});
},
_titleClick: function (event) {
console.log(this); // 'this' now refers to the widget instance.
console.log(event.widget); // so does event.widget (not necessary)
console.log(event.target); // the original element `elTitle`
},

Function for an event

I would like to create a function from this:
element.onclick = function(e){
... code ...
}
To something like this:
element.onclick = doSomething(e);
element2.onclick = doSomething(e);
function doSomething(e){
... code ...
}
In this way I could call the function multiple times but in this way e is not passed as an event so doesn't works.
How should I do this?
Not sure exactly what you mean, but you can declare a function and use it as the handler for multiple elements. The event argument will still be passed.
element.onclick = someHandler;
anotherElem.onclick = someHandler;
yetAnotherElem.onclick = someHandler;
function someHandler(e){
e = e || event;
... code ...
}
Each of the 3 elements will call the same handler, and have the event object as its argument.
EDIT: With regard to your edit, you were calling the function and assigning its return value to onclick instead of assigning the function itself.
Seems from your comment that you figured that out though. :o)
function onClickHandler(e){
//Do stuff...
}
element.onclick = onClickHandler;
I think you are asking how to create a normal function and use it in various ways?
function fnName(e) {
... code ...
}
Can be used in various ways:
element.onclick = fnName;
fnName();
//Call it however you like?
First you gotta give your function a name.
function clickEvent(e) {
... do stuff ...
}
Then attach it onto the element like this:
element.onClick = clickEvent;
When the event is triggered, the event will automatically get passed into the function.
You need to write it in the form of:
function fnName(e){
... code ...
}
and then you can call fnName and pass the variable you need.
If your function does not really need the 'e', you can write
function fnName(){
...code...
}
you can do this:
element.onclick = function(e){
myfunc();
}
function myfunc()
{
... code
}

in YUI3 is it possible to attach a single handler to multiple events?

so is something like this possible?
Y.one("input.units").on("keyup change", function(e){
...
});
the jquery equivalent is
$("input.units").bind("keyup change", function(e){
...
});
Yes, this is possible. Just pass an array of event names instead of a string:
Y.one('input.units').on(['keyup', 'change'], function (e) {
// ...
});
Why not try something like this:
var actionFunction = function(e) { /* stuff goes here */ };
node.one("input.units").on("keyup", actionFunction);
node.one("input.units").on("change", actionFunction);
EDIT: YUI supports this natively. See Ryan's answer below.
No. You could do something like this, though:
YUI().use("node", "oop", function (Y) {
var on = Y.Node.prototype.on;
function detachOne(handle) {
handle.detach();
}
Y.mix(Y.Node.prototype, {
on: function (type, fn, context) {
var args = Y.Array(arguments),
types = args[0].split(" "),
handles = [];
Y.each(types, function (type) {
args[0] = type;
handles.push(on.apply(this, args));
})
return {
detach: Y.bind(Y.each, null, handles, detachOne)
};
}
}, true);
})
This code wraps Node.on() to accept a string of space-delimited event types. It returns an object with a single method, detach, which detaches your handler from all of the events.
Note that this code only affects the Y instance inside its sandbox, so you should put it inside the function that you pass to YUI().use. It would also be easy to package it up as a module.

Categories

Resources