Pass parameters to function call in event trigger - javascript

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

Related

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>

Keeping variables between function executions (Javascript)

I have a JavaScript function that runs every time one of the many links is clicked. The functions first checks what the id of the link clicked is, then it runs an if stement. depending on the id of the link, different variables are defined.
All this works, but the problem is that some links define one variable while other links define another, I need to keep the variables defined in previous executions of the function defined for other executions of the function.
An example follows:
$(document).ready(function()
{
$(".sidebar a").click(function(event) {
event.preventDefault()
var targetID = $(this).attr("data-target")
$("#" + targetID).attr("src", $(this).attr("href"))
var element = $(this).attr("class")
if (element == "submit") {
var submit = $(this).attr("user")
alert("1")
} else if (element == "view") {
var view = $(this).attr("user")
alert("2")
}
})
window.history.replaceState({}, 'logs', '/file/path?submit=' + submit + '&' + 'view=' + view)
})
Thanks
You can use an outer function which does nothing but declare some variables and return an inner function. The inner function can access the variables from the outer scope which stay the same for every call of the function.
Example
var next = (function() {
var value = 0;
function next() {
return value++;
}
}());
console.log(next());
console.log(next());
console.log(next());
Live demo
http://jsfiddle.net/bikeshedder/UZKtE/
Define the variables in an outer scope:
$(document).ready(function () {
var submit;
var view;
$(".sidebar a").click(function (event) {
event.preventDefault();
var targetID = $(this).attr("data-target");
$("#" + targetID).attr("src", $(this).attr("href"));
var element = $(this).attr("class")
if (element == 'submit') {
submit = $(this).attr("user")
alert("1")
} else if (element == 'view') {
view = $(this).attr("user")
alert("2")
}
});
});
Create var submit and view outside of the function so it has a global scope.
You can store arbitrary data on a matched element with JQuery's .data() function.
Example:
$(this).data("submit", $(this).attr("user")); // set a value
var submit = $(this).data("submit"); // retrieve a value
This places the data in the context of JQuery's knowledge of the element, so it can be shared between function calls and even between different events.

how to get javaScript event source element?

Is there a way to retrieve the element source of an inline javaScript call?
I have a button like this:
<button onclick="doSomething('param')" id="id_button">action</button>
Note:
the button is generated from server
I cannot modify the generation process
several buttons are generated on the page, I have control only on client side.
What I have tried:
function doSomething(param){
var source = event.target || event.srcElement;
console.log(source);
}
On firebug I get event is not defined
Edit:
After some answers, an override of the event handling using jQuery is very acceptable. My issue is how to call the original onClick function with it's original prameters, and without knowing the function name.
code:
<button onclick="doSomething('param')" id="id_button1">action1</button>
<button onclick="doAnotherSomething('param1', 'param2')" id="id_button1">action2</button>.
<button onclick="doDifferentThing()" id="id_button3">action3</button>
.
.
and so on..
So the override would be:
$(document).on('click', 'button', function(e) {
e.preventDefault();
var action = $(this).attr('onclick');
/**
* What to do here to call
* - doSomething(this, 'param'); if button1 is clicked
* - doAnotherSomething(this, 'param1', 'param2'); if button2 is clicked
* - doDifferentThing(this); if button3 is clicked
* there are many buttons with many functions..
*/
});
Your html should be like this:
<button onclick="doSomething" id="id_button">action</button>
And renaming your input-paramter to event like this
function doSomething(event){
var source = event.target || event.srcElement;
console.log(source);
}
would solve your problem.
As a side note, I'd suggest taking a look at jQuery and unobtrusive javascript
You should change the generated HTML to not use inline javascript, and use addEventListener instead.
If you can not in any way change the HTML, you could get the onclick attributes, the functions and arguments used, and "convert" it to unobtrusive javascript instead by removing the onclick handlers, and using event listeners.
We'd start by getting the values from the attributes
$('button').each(function(i, el) {
var funcs = [];
$(el).attr('onclick').split(';').map(function(item) {
var fn = item.split('(').shift(),
params = item.match(/\(([^)]+)\)/),
args;
if (params && params.length) {
args = params[1].split(',');
if (args && args.length) {
args = args.map(function(par) {
return par.trim().replace(/('")/g,"");
});
}
}
funcs.push([fn, args||[]]);
});
$(el).data('args', funcs); // store in jQuery's $.data
console.log( $(el).data('args') );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button onclick="doSomething('param')" id="id_button1">action1</button>
<button onclick="doAnotherSomething('param1', 'param2')" id="id_button1">action2</button>.
<button onclick="doDifferentThing()" id="id_button3">action3</button>
That gives us an array of all and any global methods called by the onclick attribute, and the arguments passed, so we can replicate it.
Then we'd just remove all the inline javascript handlers
$('button').removeAttr('onclick')
and attach our own handlers
$('button').on('click', function() {...}
Inside those handlers we'd get the stored original function calls and their arguments, and call them.
As we know any function called by inline javascript are global, we can call them with window[functionName].apply(this-value, argumentsArray), so
$('button').on('click', function() {
var element = this;
$.each(($(this).data('args') || []), function(_,fn) {
if (fn[0] in window) window[fn[0]].apply(element, fn[1]);
});
});
And inside that click handler we can add anything we want before or after the original functions are called.
A working example
$('button').each(function(i, el) {
var funcs = [];
$(el).attr('onclick').split(';').map(function(item) {
var fn = item.split('(').shift(),
params = item.match(/\(([^)]+)\)/),
args;
if (params && params.length) {
args = params[1].split(',');
if (args && args.length) {
args = args.map(function(par) {
return par.trim().replace(/('")/g,"");
});
}
}
funcs.push([fn, args||[]]);
});
$(el).data('args', funcs);
}).removeAttr('onclick').on('click', function() {
console.log('click handler for : ' + this.id);
var element = this;
$.each(($(this).data('args') || []), function(_,fn) {
if (fn[0] in window) window[fn[0]].apply(element, fn[1]);
});
console.log('after function call --------');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button onclick="doSomething('param');" id="id_button1">action1</button>
<button onclick="doAnotherSomething('param1', 'param2')" id="id_button2">action2</button>.
<button onclick="doDifferentThing()" id="id_button3">action3</button>
<script>
function doSomething(arg) { console.log('doSomething', arg) }
function doAnotherSomething(arg1, arg2) { console.log('doAnotherSomething', arg1, arg2) }
function doDifferentThing() { console.log('doDifferentThing','no arguments') }
</script>
Cross-Browser solution
I believe the solution by #slipset was correct, and it doesn't need jQuery, BUT it wasn't cross-browser ready.
According to Javascript.info, events (when referenced outside markup events) are cross-browser ready once you assure it's defined with this simple line: event = event || window.event.
So the complete cross-browser ready function would look like this:
function logMySource(param){
event = event || window.event;
var source = event.target || event.srcElement;
console.log("sourceID= "+source.id,"\nsourceTagName= "+source.tagName,"\nparam= "+param);
}
<button onclick="logMySource('myVariable')" id="myID">action</button>
Try it!
I've included returns of useful information of the source.
You can pass this when you call the function
<button onclick="doSomething('param',this)" id="id_button">action</button>
<script>
function doSomething(param,me){
var source = me
console.log(source);
}
</script>
Try something like this:
<html>
<body>
<script type="text/javascript">
function doSomething(event) {
var source = event.target || event.srcElement;
console.log(source);
alert('test');
if(window.event) {
// IE8 and earlier
// doSomething
} else if(e.which) {
// IE9/Firefox/Chrome/Opera/Safari
// doSomething
}
}
</script>
<button onclick="doSomething('param')" id="id_button">
action
</button>
</body>
</html>
USE .live()
$(selector).live(events, data, handler);
As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers.
$(document).on(events, selector, data, handler);

jQuery, associative array and binding events

I would like to bind the same event to 3 checkboxes but with a different target each time:
var checkboxes = {
'selector1' : 'target1',
'selector2' : 'target2',
'selector3' : 'target3',
};
for (selector in checkboxes) {
var target = checkboxes[selector];
if (jQuery(selector).is(':checked')) {
jQuery(target).show();
}
else {
jQuery(target).hide();
}
jQuery(selector).bind('change', function() {
if ($(this).is(':checked')) {
jQuery(target).show();
}
else {
jQuery(target).hide();
}
});
};
But it doesn't work: on "change", the 3 selectors show/hide the 3rd target.
That's because the code in the event handler will use the variable target, not the value of the variable as it was when the event handler was created. When the event hander runs, the variable will contain the last value used in the loop.
Use an anonymous function to create a closure, that captures the value of the variable:
for (selector in checkboxes) {
var target = checkboxes[selector];
if (jQuery(selector).is(':checked')) {
jQuery(target).show();
} else {
jQuery(target).hide();
}
(function(target){
jQuery(selector).bind('change', function() {
if ($(this).is(':checked')) {
jQuery(target).show();
} else {
jQuery(target).hide();
}
});
})(target);
};
Side note: You can use the toggle method to make the code simpler:
for (selector in checkboxes) {
var target = checkboxes[selector];
jQuery(target).toggle(jQuery(selector).is(':checked'));
(function(target){
jQuery(selector).bind('change', function() {
jQuery(target).toggle($(this).is(':checked'));
});
})(target);
};
It doesn't work because target isn't scoped inside of a function. Blocks DO NOT provide scope in javascript.
But I've reduced it down for you and hopefully this will work out:
$.each(checkboxes, function(selector, target) {
$(selector).change(function () {
$(target).toggle($(selector).is(':checked'));
}).trigger('change');
});
target scope is the problem !
You could have simpler code:
use data for the handler
use .toggle(condition)
$.each(checkboxes, function(selector, target) {
$(selector).on('change', {target:target}, function (evt) {
$(evt.data.target).toggle($(this).is(':checked'));
}).change();
});

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

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.

Categories

Resources