48

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..
   */
});
ilyes kooli
  • 11,959
  • 14
  • 50
  • 79

6 Answers6

37

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

slipset
  • 2,960
  • 2
  • 21
  • 17
  • 2
    **the button is generated from server AND I cannot modify the generation process** – ilyes kooli May 03 '12 at 09:32
  • @skafandri: Why not, if you can modify the script? Apart from that, he is right when he suggests a look on [unobtrusive JavaScript](http://en.wikipedia.org/wiki/Unobtrusive_JavaScript) - that reading will show you that the HTML is bad practice. – Bergi May 03 '12 at 09:41
  • I know! in my own scripts I always use unobtrusive javascript, but in this case I'm building a frontend application of an existing framework.. – ilyes kooli May 03 '12 at 09:44
  • Don't forget to use .value property to get the actual value of the item. Such as console.log(source.value); – Sydwell Feb 14 '13 at 10:12
  • Would only add Cross-browser functionality as mentioned in my answer. – DavidTaubmann Oct 17 '16 at 19:39
14

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>
adeneo
  • 312,895
  • 29
  • 395
  • 388
  • there are many buttons, some buttons are generated after ajax calls... I have no clue about IDs (to explain more, I'm writing a Magento module..) – ilyes kooli May 03 '12 at 09:42
  • Right now your function has no scope, you need to somehow add scope, and if you can't modify the button element, you will have to override it somehow, see my modified answer. – adeneo May 03 '12 at 09:45
  • I am ok with a jQuery override, if only I find out how to call the original onClick function (doSomething is an exemple, there are several functions with several params) – ilyes kooli May 03 '12 at 09:47
  • That depends, you can get the onclick function with `$(this).attr('onclick');` , and just construct a new function call with `this` included to get the scope, and you would probably have to use eval() to run that function, and then it's a matter of where the functions are in the DOM, assuming they are inline etc. It's all in all not a very good solution IMO. – adeneo May 03 '12 at 09:55
  • Please check my EDIT :-), it constructs the function name from the onclick function, and adds `this`, I'd probably redefine the functions if possible instead, to avoid the eval() call, but should work. – adeneo May 03 '12 at 10:06
  • As a sidenote, the latest edit seems to run the function twice in Chrome ? You will have to try this out in different browsers, and it will take some bending and twisting to make it work. – adeneo May 03 '12 at 10:08
3

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>
Sethunath K M
  • 4,702
  • 3
  • 28
  • 42
3

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.

DavidTaubmann
  • 3,223
  • 2
  • 34
  • 43
2

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>
mit
  • 11,083
  • 11
  • 50
  • 74
srini
  • 876
  • 3
  • 12
  • 23
-1

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);  
Pranav
  • 8,563
  • 4
  • 26
  • 42
  • Why should he use live() instead of normal bind()? – Bergi May 03 '12 at 09:37
  • I have several buttons on the page, buttons have different onClick functions, I can use $('body').on(event,'button', function(){}); is ok, can you tell me how I can execute the onClick and passing $(this) to it? – ilyes kooli May 03 '12 at 09:38
  • @Bergi yes he is right, some buttons are retrieved after Ajax calls. – ilyes kooli May 03 '12 at 09:38
  • @Pranav: I know the difference, so I wondered why you would recommend the more complicated method when the question was just about passing the event object. OK, since we know there are many buttons and some are even dynamically appended, it is obviosly advisable. – Bergi May 03 '12 at 09:52
  • @Bergi: yes you are right,since the buttons are dynamically generated,so i advised to go for live() instead of bind(). – Pranav May 03 '12 at 10:00