Por que uma propriedade onclick definida com setAttribute não funciona no IE?

Corri para este problema hoje, postando no caso de alguém ter o mesmo problema.

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

Acontece que para obter o IE para executar um onclick em um elemento gerado dinamicamente, não podemos usar setAttribute. Em vez disso, precisamos definir a propriedade onclick no object com uma function anônima envolvendo o código que queremos executar.

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

IDEIAS MÁS:

Você pode fazer

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

mas vai quebrar no IE no modo não-padrão de acordo com @scunliffe.

Você não pode fazer isso

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

porque ele é executado imediatamente e define o resultado de runCommand () para ser o valor do atributo onClick, nem você pode fazer

 execBtn.setAttribute("onclick", runCommand); 

Para fazer isso funcionar tanto no FF quanto no IE, você deve escrever nos dois sentidos:

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

graças a este post .

ATUALIZAÇÃO : Isso é para demonstrar que às vezes é necessário usar setAttribute! Este método funciona se você precisar pegar o atributo onclick original do HTML e adicioná-lo ao evento onclick, para que ele não seja substituído:

 // 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 } 

Há uma grande coleção de atributos que você não pode definir no IE usando .setAttribute (), que inclui todos os manipuladores de events in-line.

Veja aqui para mais detalhes:

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

funciona bem!

usar os dois lados parece ser desnecessário agora:

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

aparentemente funciona em todos os navegadores atuais.

testado no Firefox atual, IE, Safari, Opera, Chrome no Windows; Firefox e Epiphany no Ubuntu; não testado em sistemas Mac ou móveis.

  • Craig: Eu tentaria “document.getElementById (ID) .type = ‘password’;
  • Alguém já verificou a abordagem “AddEventListener” com diferentes mecanismos?

Esta é uma function incrível para a vinculação de events compatível com vários navegadores.

Consegui-lo de http://js.isite.net.au/snippets/addevent

Com ele você pode apenas fazer Events.addEvent(element, event, function); e esteja livre de preocupações!

Por exemplo: ( 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); 

Aqui está a 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}; })(); 

Se você não quiser usar uma function tão grande, isso deve funcionar para quase todos os navegadores, incluindo o IE:

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

Em resposta à pergunta de Craig. Você terá que criar um novo elemento e copiar os atributos do elemento antigo. Esta function deve fazer o trabalho: ( fonte )

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

Ou você poderia usar o jQuery e evitar todos esses problemas:

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

O jQuery também cuidará de todos os problemas entre navegadores.

Na verdade, até onde sei, os manipuladores de events embutidos criados dinamicamente funcionam perfeitamente com o Internet Explorer 8 quando criados com o comando x.setAttribute() ; você apenas tem que posicioná-los corretamente dentro do seu código JavaScript. Eu tropecei com a solução para o seu problema (e meu) aqui .

Quando movi todas as minhas instruções contendo x.appendChild() para suas posições corretas (ou seja, imediatamente após o último comando setAttribute dentro de seus grupos), descobri que CADA único setAttribute funcionava no IE8 como deveria, incluindo todas as inputs de formulário atributos (incluindo os atributos “name” e “type”, bem como os manipuladores de events “onclick”).

Achei isso bastante notável, já que tudo que eu tinha no IE antes de fazer isso era lixo renderizado na canvas e um erro após o outro. Além disso, descobri que cada setAttribute ainda funcionava nos outros navegadores também, então, se você se lembrar dessa simples prática de codificação, você será bom na maioria dos casos.

No entanto, esta solução não funcionará se você tiver que alterar qualquer atributo na hora, já que eles não podem ser alterados no IE depois que o elemento HTML tiver sido anexado ao DOM; Nesse caso, imagino que seria necessário excluir o elemento do DOM e, em seguida, recriá-lo e seus atributos (na ordem correta, é claro!) para que funcionem corretamente e não apresentem erros.

Escreva a function em linha e o intérprete é inteligente o suficiente para saber que você está escrevendo uma function. Faça assim, e assume que é apenas uma string (o que tecnicamente é).

 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+');'); } } 

Você tentou:

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

Em alguns casos, os exemplos listados aqui não funcionaram para mim no Internet Explorer.

Desde que você tem que definir a propriedade com um método como este (sem parênteses)

 HtmlElement.onclick = myMethod; 

não funcionará se você tiver que passar um nome de object ou mesmo parâmetros. Para o Internet Explorer, você deve criar um novo object em tempo de execução:

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

Funciona também em outros navegadores.

Não relevante para o problema onclick, mas também relacionado:

Para atributos html cujo nome colida com palavras reservadas do javascript, um nome alternativo é escolhido, por exemplo.

, mas div.className ou , mas label.htmlFor .

Em navegadores razoáveis, isso não afeta o setAttribute . Então no gecko e no webkit você chamaria div.setAttribute('class', 'foo') , mas no IE você tem que usar o nome da propriedade javascript, então div.setAttribute('className', 'foo') .

Você já considerou um ouvinte de evento em vez de definir o atributo? Entre outras coisas, permite que você passe parâmetros, o que foi um problema que tive ao tentar fazer isso. Você ainda tem que fazer isso duas vezes para o IE e 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"); 

Apenas deixe o evento ser um nome como “clique” ou “mouseover”.

Eu fiz isso para contornar isso e seguir em frente, no meu caso eu não estou usando um elemento ‘input’, ao invés disso eu uso uma imagem, quando eu tentei configurar o atributo “onclick” para essa imagem eu tive o mesmo problema, então Eu tentei envolver a imagem com um elemento “a” e fazendo o ponto de referência para a function como esta.

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

Tente este execBtn.onclick = function () {eval (‘runCommand ()’)};

Para mim funciona.