Why does an onclick property set with setAttribute fail to work in IE?

JavascriptInternet Explorer

Javascript Problem Overview


Ran into this problem today, posting in case someone else has the same issue.

var execBtn = document.createElement('input');
execBtn.setAttribute("type", "button");
execBtn.setAttribute("id", "execBtn");
execBtn.setAttribute("value", "Execute");
execBtn.setAttribute("onclick", "runCommand();");

Turns out to get IE to run an onclick on a dynamically generated element, we can't use setAttribute. Instead, we need to set the onclick property on the object with an anonymous function wrapping the code we want to run.

execBtn.onclick = function() { runCommand() };

BAD IDEAS:

You can do

execBtn.setAttribute("onclick", function() { runCommand() });

but it will break in IE in non-standards mode according to @scunliffe.

You can't do this at all

execBtn.setAttribute("onclick", runCommand() ); 

because it executes immediately, and sets the result of runCommand() to be the onClick attribute value, nor can you do

execBtn.setAttribute("onclick", runCommand);

Javascript Solutions


Solution 1 - Javascript

to make this work in both FF and IE you must write both ways:


button_element.setAttribute('onclick','doSomething();'); // for FF
button_element.onclick = function() {doSomething();}; // for IE

thanks to this post.

UPDATE: This is to demonstrate that sometimes it is necessary to use setAttribute! This method works if you need to take the original onclick attribute from the HTML and add it to the onclick event, so that it doesn't get overridden:

// get old onclick attribute
var onclick = button_element.getAttribute("onclick");  

// if onclick is not a function, it's not IE7, so use setAttribute
if(typeof(onclick) != "function") { 
    button_element.setAttribute('onclick','doSomething();' + onclick); // for FF,IE8,Chrome

// if onclick is a function, use the IE7 method and call onclick() in the anonymous function
} else {
    button_element.onclick = function() { 
        doSomething();
        onclick();
    }; // for IE7
}

Solution 2 - Javascript

There is a LARGE collection of attributes you can't set in IE using .setAttribute() which includes every inline event handler.

See here for details:

http://webbugtrack.blogspot.com/2007/08/bug-242-setattribute-doesnt-always-work.html

Solution 3 - Javascript

works great!

using both ways seem to be unnecessary now:

execBtn.onclick = function() { runCommand() };

apparently works in every current browser.

tested in current Firefox, IE, Safari, Opera, Chrome on Windows; Firefox and Epiphany on Ubuntu; not tested on Mac or mobile systems.

  • Craig: I'd try "document.getElementById(ID).type='password';
  • Has anyone checked the "AddEventListener" approach with different engines?

Solution 4 - Javascript

Or you could use jQuery and avoid all those issues:

var execBtn = $("<input>", {
       type: "button",
       id: "execBtn",
       value: "Execute"
    })
    .click(runCommand);        

jQuery will take care of all the cross-browser issues as well.

Solution 5 - Javascript

This is an amazing function for cross-browser compatible event binding.

Got it from http://js.isite.net.au/snippets/addevent

With it you can just do Events.addEvent(element, event, function); and be worry free!

For example: (http://jsfiddle.net/Zxeka/)

function hello() {
    alert('Hello');
}

var button = document.createElement('input');
button.value = "Hello";
button.type = "button";

Events.addEvent(input_0, "click", hello);

document.body.appendChild(button);

Here's the function:

// We create a function which is called immediately,
// returning the actual function object.  This allows us to
// work in a separate scope and only return the functions
// we require.
var Events = (function() {

  // For DOM2-compliant browsers.
  function addEventW3C(el, ev, f) {
    // Since IE only supports bubbling, for
    // compatibility we can't use capturing here.
    return el.addEventListener(ev, f, false);
  }

  function removeEventW3C(el, ev, f) {
    el.removeEventListener(ev, f, false);
  }

  // The function as required by IE.
  function addEventIE(el, ev, f) {
    // This is to work around a bug in IE whereby the
    // current element doesn't get passed as context.
    // We pass it via closure instead and set it as the
    // context using call().
    // This needs to be stored for removeEvent().
    // We also store the original wrapped function as a
    // property, _w.
    ((el._evts = el._evts || [])[el._evts.length]
        = function(e) { return f.call(el, e); })._w = f;

    // We prepend "on" to the event name.
    return el.attachEvent("on" + ev,
        el._evts[el._evts.length - 1]);
  }

  function removeEventIE(el, ev, f) {
    for (var evts = el._evts || [], i = evts.length; i--; )
      if (evts[i]._w === f)
        el.detachEvent("on" + ev, evts.splice(i, 1)[0]);
  }

  // A handler to call all events we've registered
  // on an element for legacy browsers.
  function addEventLegacyHandler(e) {
    var evts = this._evts[e.type];
    for (var i = 0; i < evts.length; ++i)
      if (!evts[i].call(this, e || event))
        return false;
  }

  // For older browsers.  We basically reimplement
  // attachEvent().
  function addEventLegacy(el, ev, f) {
    if (!el._evts)
      el._evts = {};

    if (!el._evts[ev])
      el._evts[ev] = [];

    el._evts[ev].push(f);

    return true;
  }

  function removeEventLegacy(el, ev, f) {
    // Loop through the handlers for this event type
    // and remove them if they match f.
    for (var evts = el._evts[ev] || [], i = evts.length; i--; )
      if (evts[i] === f)
        evts.splice(i, 1);
  }

  // Select the appropriate functions based on what's
  // available on the window object and return them.
  return window.addEventListener
      ? {addEvent: addEventW3C, removeEvent: removeEventW3C}
      : window.attachEvent
          ? {addEvent: addEventIE, removeEvent: removeEventIE}
          : {addEvent: addEventLegacy, removeEvent: removeEventLegacy};
})();

If you don't want to use such a big function, this should work for almost all browsers, including IE:

if (el.addEventListener) { 
    el.addEventListener('click', function, false); 
} else if (el.attachEvent) { 
    el.attachEvent('onclick', function); 
} 

In response to Craig's question. You're going to have to make a new element and copy over the attributes of the old element. This function should do the job: (source)

function changeInputType(oldObject, oType) {
  var newObject = document.createElement('input');
  newObject.type = oType;
  if(oldObject.size) newObject.size = oldObject.size;
  if(oldObject.value) newObject.value = oldObject.value;
  if(oldObject.name) newObject.name = oldObject.name;
  if(oldObject.id) newObject.id = oldObject.id;
  if(oldObject.className) newObject.className = oldObject.className;
  oldObject.parentNode.replaceChild(newObject,oldObject);
  return newObject;
}

Solution 6 - Javascript

Actually, as far as I know, dynamically created inline event-handlers DO work perfectly within Internet Explorer 8 when created with the x.setAttribute() command; you just have to position them properly within your JavaScript code. I stumbled across the solution to your problem (and mine) here.

When I moved all of my statements containing x.appendChild() to their correct positions (i.e., immediately following the last setAttribute command within their groups), I found that EVERY single setAttribute worked in IE8 as it was supposed to, including all form input attributes (including "name" and "type" attributes, as well as my "onclick" event-handlers).

I found this quite remarkable, since all I got in IE before I did this was garbage rendered across the screen, and one error after another. In addition, I found that every setAttribute still worked within the other browsers as well, so if you just remember this simple coding-practice, you'll be good to go in most cases.

However, this solution won't work if you have to change any attributes on the fly, since they cannot be changed in IE once their HTML element has been appended to the DOM; in this case, I would imagine that one would have to delete the element from the DOM, and then recreate it and its attributes (in the correct order, of course!) for them to work properly, and not throw any errors.

Solution 7 - Javascript

Write the function inline, and the interpreter is smart enough to know you're writing a function. Do it like this, and it assumes it's just a string (which it technically is).

Solution 8 - Javascript

function CheckBrowser(){
	if(navigator.userAgent.match(/Android/i)!=null||
		navigator.userAgent.match(/BlackBerry/i)!=null||
		navigator.userAgent.match(/iPhone|iPad|iPod/i)!=null||
		navigator.userAgent.match(/Nokia/i)!=null||
		navigator.userAgent.match(/Opera M/i)!=null||
		navigator.userAgent.match(/Chrome/i)!=null)
		{
			return 'OTHER';
	}else{
			return 'IE';
	}
}


function AddButt(i){
	var new_butt = document.createElement('input');
	new_butt.setAttribute('type','button');
	new_butt.setAttribute('value','Delete Item');
	new_butt.setAttribute('id', 'answer_del_'+i);
	if(CheckBrowser()=='IE'){
		new_butt.setAttribute("onclick", function() { DelElemAnswer(i) });
	}else{
		new_butt.setAttribute('onclick','javascript:DelElemAnswer('+i+');');
	}
}

Solution 9 - Javascript

In some cases the examples listed here didn't work out for me in Internet Explorer.

Since you have to set the property with a method like this (without brackets)

HtmlElement.onclick = myMethod;

it won't work if you have to pass an object-name or even parameters. For the Internet Explorer you should create a new object in runtime:

HtmlElement.onclick = new Function('myMethod(' + someParameter + ')');

Works also on other browsers.

Solution 10 - Javascript

Did you try:

execBtn.setAttribute("onclick", function() { runCommand() });

Solution 11 - Javascript

Not relevant to the onclick issue, but also related:

For html attributes whose name collide with javascript reserved words, an alternate name is chosen, eg. <div class=''>, but div.className, or <label for='...'>, but label.htmlFor.

In reasonable browsers, this doesn't affect setAttribute. So in gecko and webkit you'd call div.setAttribute('class', 'foo'), but in IE you have to use the javascript property name instead, so div.setAttribute('className', 'foo').

Solution 12 - Javascript

Have you considered an event listener rather than setting the attribute? Among other things, it lets you pass parameters, which was a problem I ran into when trying to do this. You still have to do it twice for IE and Mozilla:

function makeEvent(element, callback, param, event) {
	function local() {
		return callback(param);
	}

	if (element.addEventListener) {
		//Mozilla
		element.addEventListener(event,local,false);
	} else if (element.attachEvent) {
		//IE
		element.attachEvent("on"+event,local);
	}
}

makeEvent(execBtn, alert, "hey buddy, what's up?", "click");

Just let event be a name like "click" or "mouseover".

Solution 13 - Javascript

I did this to get around it and move on, in my case I'm not using an 'input' element, instead I use an image, when I tried setting the "onclick" attribute for this image I experienced the same problem, so I tried wrapping the image with an "a" element and making the reference point to the function like this.

var rowIndex = 1;
var linkDeleter = document.createElement('a');
linkDeleter.setAttribute('href', "javascript:function(" + rowIndex + ");");

var imgDeleter = document.createElement('img');
imgDeleter.setAttribute('alt', "Delete");
imgDeleter.setAttribute('src', "Imagenes/DeleteHS.png");
imgDeleter.setAttribute('border', "0");

linkDeleter.appendChild(imgDeleter);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAeonView Question on Stackoverflow
Solution 1 - JavascriptShaike KatzView Answer on Stackoverflow
Solution 2 - JavascriptscunliffeView Answer on Stackoverflow
Solution 3 - JavascriptTitusView Answer on Stackoverflow
Solution 4 - JavascriptcdmckayView Answer on Stackoverflow
Solution 5 - JavascriptAcornView Answer on Stackoverflow
Solution 6 - JavascriptKarlView Answer on Stackoverflow
Solution 7 - JavascriptSietseView Answer on Stackoverflow
Solution 8 - JavascriptMedyancevView Answer on Stackoverflow
Solution 9 - JavascriptabendpostView Answer on Stackoverflow
Solution 10 - JavascriptLarkView Answer on Stackoverflow
Solution 11 - JavascriptkchView Answer on Stackoverflow
Solution 12 - JavascriptDavid BergerView Answer on Stackoverflow
Solution 13 - JavascriptMarkoView Answer on Stackoverflow