I need to access event context AND object context in event handler - javascript

I've this piece of code:
function ActivityDialog(_divId, _title) {
function addButton() {
var buttonElement = document.createElement('input');
buttonElement.setAttribute('type','button');
buttonElement.setAttribute('class','button');
buttonElement.setAttribute('id','updateButton-' + id););
buttonElement.onclick = this.updateAction;
};
function updateAction() {
var buttonId = this.id; // correct: this is the Button
this.sendUpdateRequest(stringUrl); // not defined: Need to reference the current ActivityDialog!!!
};
function sendUpdateRequest(url) {
// something...
};
}
As you can see the problem is when I call function sendUpdateRequest; how can I, at the same time, retrieve button infos and call a function?

You might try this...
function ActivityDialog(_divId, _title) {
// Store the ActivityDialog context
var self = this;
function addButton() {
var buttonElement = document.createElement('input');
buttonElement.setAttribute('type','button');
buttonElement.setAttribute('class','button');
buttonElement.setAttribute('id','updateButton-' + id););
buttonElement.onclick = this.updateAction;
};
function updateAction() {
var buttonId = this.id;
self.sendUpdateRequest(stringUrl); // <---------------------
};
function sendUpdateRequest(url) {
// something...
};
}
Because your using updateAction as a event handler, you correctly see that this will be the button that generates the event. Storing the initial context of the ActivityDialog will allow you to maintain access to it even within event handlers.

Related

Object.defineProperty to override HTMLElement eventhandler not work on event attributes

I want to override HTMLElement's on-event handler method with Object.defineProperty.
here is the code.
var desc = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'onclick');
var originalGet = desc.get;
desc.get = function () {
console.log('get onclick');
return originalGet.apply(this, arguments);
}
var originalSet = desc.set;
desc.set = function() {
console.log('set onclick', arguments);
return originalSet.apply(this, arguments);
}
The code will work when I add event listener in this way,
var btn = document.getElementById("btn");
btn.onclick = function() {
console.log("button clicked");
}
But will not work when I define the onclick handler in html inline.
<button onclick="btnClick(event)">ok</button>
The getter is called, but setter is not called.
I can't figure out why?

jQuery plugin instances variable with event handlers

I am writing my first jQuery plugin which is a tree browser. It shall first show the top level elements and on click go deeper and show (depending on level) the children in a different way.
I got this up and running already. But now I want to implement a "back" functionality and for this I need to store an array of clicked elements for each instance of the tree browser (if multiple are on the page).
I know that I can put instance private variables with "this." in the plugin.
But if I assign an event handler of the onClick on a topic, how do I get this instance private variable? $(this) is referencing the clicked element at this moment.
Could please anyone give me an advise or a link to a tutorial how to get this done?
I only found tutorial for instance specific variables without event handlers involved.
Any help is appreciated.
Thanks in advance.
UPDATE: I cleaned out the huge code generation and kept the logical structure. This is my code:
(function ($) {
$.fn.myTreeBrowser = function (options) {
clickedElements = [];
var defaults = {
textColor: "#000",
backgroundColor: "#fff",
fontSize: "1em",
titleAttribute: "Title",
idAttribute: "Id",
parentIdAttribute: "ParentId",
levelAttribute: "Level",
treeData: {}
};
var opts = $.extend({}, $.fn.myTreeBrowser.defaults, options);
function getTreeData(id) {
if (opts.data) {
$.ajax(opts.data, { async: false, data: { Id: id } }).success(function (resultdata) {
opts.treeData = resultdata;
});
}
}
function onClick() {
var id = $(this).attr('data-id');
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, id);
}
function handleOnClick(parentContainer, id) {
if (opts.onTopicClicked) {
opts.onTopicClicked(id);
}
clickedElements.push(id);
if (id) {
var clickedElement = $.grep(opts.treeData, function (n, i) { return n[opts.idAttribute] === id })[0];
switch (clickedElement[opts.levelAttribute]) {
case 1:
renderLevel2(parentContainer, clickedElement);
break;
case 3:
renderLevel3(parentContainer, clickedElement);
break;
default:
debug('invalid level element clicked');
}
} else {
renderTopLevel(parentContainer);
}
}
function getParentContainer(elem) {
return $(elem).parents('div.myBrowserContainer').parents()[0];
}
function onBackButtonClick() {
clickedElements.pop(); // remove actual element to get the one before
var lastClickedId = clickedElements.pop();
var parentContainer = getParentContainer($(this));
handleOnClick(parentContainer, lastClickedId);
}
function renderLevel2(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderLevel3(parentContainer, selectedElement) {
$(parentContainer).html('');
var browsercontainer = $('<div>').addClass('myBrowserContainer').appendTo(parentContainer);
//... rendering the div ...
// for example like this with a onClick handler
var div = $('<div>').attr('data-id', element[opts.idAttribute]).addClass('fct-bs-col-md-4 pexSubtopic').on('click', onClick).appendTo(subtopicList);
// ... rendering the tree
var backButton = $('<button>').addClass('btn btn-default').text('Back').appendTo(browsercontainer);
backButton.on('click', onBackButtonClick);
}
function renderTopLevel(parentContainer) {
parentContainer.html('');
var browsercontainer = $('<div>').addClass('fct-page-pa fct-bs-container-fluid pexPAs myBrowserContainer').appendTo(parentContainer);
// rendering the top level display
}
getTreeData();
//top level rendering! Lower levels are rendered in event handlers.
$(this).each(function () {
renderTopLevel($(this));
});
return this;
};
// Private function for debugging.
function debug(debugText) {
if (window.console && window.console.log) {
window.console.log(debugText);
}
};
}(jQuery));
Just use one more class variable and pass this to it. Usually I call it self. So var self = this; in constructor of your plugin Class and you are good to go.
Object oriented way:
function YourPlugin(){
var self = this;
}
YourPlugin.prototype = {
constructor: YourPlugin,
clickHandler: function(){
// here the self works
}
}
Check this Fiddle
Or simple way of passing data to eventHandler:
$( "#foo" ).bind( "click", {
self: this
}, function( event ) {
alert( event.data.self);
});
You could use the jQuery proxy function:
$(yourElement).bind("click", $.proxy(this.yourFunction, this));
You can then use this in yourFunction as the this in your plugin.

Override (wrap) an existing jQuery click event with another in javascript

Say I have an existing button and attach a click to it via jQuery:
var $button = $('#test').click(function () { console.log('original function') });
Now, say I want to override that click so that I can add some logic to the function before and after it. I have tried binding and wrapping using the functions below.
Function.prototype.bind = function () {
var fn = this;
var args = Array.prototype.slice.call(arguments);
var object = args.shift();
return function () {
return fn.apply(object, args.concat(Array.prototype.slice.call(arguments)));
}
}
function wrap(object, method, wrapper) {
var fn = object[method];
return object[method] = function() {
return wrapper.apply(this, [fn.bind(this)].concat(
Array.prototype.slice.call(arguments)));
}
}
so I call wrap with the object that the method is a property of, the method and an anonymous function that I want to execute instead. I thought:
wrap($button 'click', function (click) {
console.log('do stuff before original function');
click();
console.log('do stuff after original function');
});
This only calls the original function. I have used this approach on a method of an object before with success. Something like: See this Plunker
Can anyone help me do this with my specific example please?
Thanks
You could create a jQuery function that gets the original event handler function from data, removes the click event, then adds a new event handler. This function would have two parameters (each functions) of before and after handlers.
$(function() {
jQuery.fn.wrapClick = function(before, after) {
// Get and store the original click handler.
// TODO: add a conditional to check if click event exists.
var _orgClick = $._data(this[0], 'events').click[0].handler,
_self = this;
// Remove click event from object.
_self.off('click');
// Add new click event with before and after functions.
return _self.click(function() {
before.call(_self);
_orgClick.call(_self);
after.call(_self);
});
};
var $btn = $('.btn').click(function() {
console.log('original click');
});
$btn.wrapClick(function() {
console.log('before click');
}, function() {
console.log('after click');
});
});
Here is a Codepen
After a long search I reached the same answer as #Corey, here is a similar way of doing it considering multiple events:
function wrap(object, method, wrapper) {
var arr = []
var events = $._data(object[0], 'events')
if(events[method] && events[method].length > 0){ // add all functions to array
events[method].forEach(function(obj){
arr.push(obj.handler)
})
}
if(arr.length){
function processAll(){ // process all original functions in the right order
arr.forEach(function(func){
func.call(object)
})
}
object.off(method).on(method, function(e){wrapper.call(object,processAll)}) //unregister previous events and call new method passing old methods
}
}
$(function(){
$('#test').click(function () { console.log('original function 1') });
var $button = $('#test').click(function () { console.log('original function 2') });
wrap($button, 'click', function (click,e) {
console.log('do stuff before original functions');
click()
console.log('do stuff after original functions');
});
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id='test'>click me</div>

How to add an eventlistener to a button javascript

How can I add an eventlistener to my button? When the button is clicked the function speler1gooien has to run. When I run my example code below the function speler1gooien is fired on page load. What is the correct way to run my function only when clicked?
window.onload = function () {
buttonSpeler1 = document.getElementById("buttonspeler1");
buttonSpeler2 = document.getElementById("buttonspeler2");
tafelp2.src = "images/rechts_" + aantalBekersP2 + ".png";
resultaat = document.getElementById("resultaat");
buttonSpeler1.addEventListener("click", speler1gooien());
};
var speler1gooien = function() {
// some code
}
Remove the parenthesis in the call speler1gooien() because it's causing your function to be executed immediately, and the return value is being passed as the click event handler.
buttonSpeler1.addEventListener("click", speler1gooien);
// ^ removed ()
By removing the parenthesis, you are passing the function object, instead of executing it.
You are trying to attach function speler1gooien as onclick handler, but your code attaches as handler the result of spele1gooien function. Just remove () following name of that function
window.onload = function () {
buttonSpeler1 = document.getElementById("buttonspeler1");
buttonSpeler2 = document.getElementById("buttonspeler2");
tafelp2.src = "images/rechts_" + aantalBekersP2 + ".png";
resultaat = document.getElementById("resultaat");
buttonSpeler1.addEventListener("click", speler1gooien);
};
var speler1gooien = function() {
// some code
}

Pass parameters to function call in event trigger

Right now I have this
jQuery('.widget-prop').keyup(function() {
var prop = jQuery(this).attr('id');
var val = jQuery(this).val();
stuff;
}
and
jQuery('.widget-prop').click(function() {
var prop = jQuery(this).attr('id');
var val = jQuery(this).val();
stuff;
}
two functions are the same, so I'd like to simplify it by defining external function and calling it with
jQuery('.widget-prop').click('myFunction');
but how would I pass parameters to myFunction?
function myFunction(element) {
stuff;
}
Thanks
Get rid of those quotes...
jQuery('.widget-prop').click(myFunction);
Firstly you can combine both your handlers into one:
jQuery('.widget-prop').on('click keyup', function() {
var prop = jQuery(this).attr('id');
var val = jQuery(this).val();
// stuff;
});
Or for older versions of jQuery:
jQuery('.widget-prop').bind('click keyup', function() { // ... });
Secondly, jQuery will automatically apply the current element to any function you provide, so the this keyword will still be the element raising the event when you do this:
jQuery('.widget-prop').on('click keyup', myHandler);
function myHandler() {
var prop = jQuery(this).attr('id');
var val = jQuery(this).val();
// stuff;
}
Use the .on function instead of the shortcut .click and pass all required events as paramter:
jQuery('.widget-prop').on('click keyup', function() {
var prop = jQuery(this).attr('id');
var val = jQuery(this).val();
//stuff;
});
EDIT: deprecated => shortcut
In the context of MyFunction this is your clicked element. Additionally, the click handler takes 1 argument, so your function signature should be function MyFucntion(event) where the event argument holds data about the action (e.g. event.target is the clicked element).
so this is how you would define your handler:
function myFunction(event){
var element = event.target;
// Note: event.target == this
//do stuff
}
and this is how you would bind it to the event:
$(".widget-prop").click(myFunction);

Categories

Resources