关于javascript:为什么使用setAttribute设置的onclick属性在IE中无法正常工作?

关于javascript:为什么使用setAttribute设置的onclick属性在IE中无法正常工作?

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

今天遇到了这个问题,以防其他人遇到同样的问题而发布。

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

结果是让IE在动态生成的元素上运行onclick,我们不能使用setAttribute。相反,我们需要使用匿名函数package对象上的onclick属性,该函数package要运行的代码。

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

错误提示:

您可以

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

但是根据@scunliffe,它将以非标准模式在IE中中断。

您根本无法做到这一点

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

因为它立即执行,并将runCommand()的结果设置为onClick属性值,所以也不能执行

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

要使此功能在FF和IE中均能正常工作,您必须同时编写以下两种方式:

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

感谢这篇文章。

更新:
这是为了证明有时需要使用setAttribute!如果您需要从HTML中获取原始的onclick属性并将其添加到onclick事件中,以使其不会被覆盖,则可以使用此方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 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
}

效果很好!

现在似乎不需要使用两种方式:

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

显然可以在每个当前浏览器中使用。

在Windows上的当前Firefox,IE,Safari,Opera,Chrome中进行了测试;火狐浏览器
和Ubuntu上的Epiphany;未在Mac或移动系统上进行测试。

  • Craig:我会尝试" document.getElementById(ID).type = \\'password \\';
  • 有没有人使用不同的引擎检查过" AddEventListener"方法?

有一个无法使用.setAttribute()在IE中设置的大型属性集合,其中包括每个内联事件处理程序。

有关详细信息,请参见此处:

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


对于跨浏览器兼容的事件绑定,这是一个了不起的功能。

从http://js.isite.net.au/snippets/addevent

获得它

有了它,您只需执行Events.addEvent(element, event, function);并放心!

例如:(http://jsfiddle.net/Zxeka/)

1
2
3
4
5
6
7
8
9
10
11
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);

这是函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// 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};
})();

如果您不想使用这么大的功能,则该功能应该适用于几乎所有浏览器,包括IE:

1
2
3
4
5
if (el.addEventListener) {
    el.addEventListener('click', function, false);
} else if (el.attachEvent) {
    el.attachEvent('onclick', function);
}

回答Craig的问题。您将必须制作一个新元素并复制旧元素的属性。此功能应该可以完成:(source)

1
2
3
4
5
6
7
8
9
10
11
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;
}

或者您可以使用jQuery并避免所有这些问题:

1
2
3
4
5
6
var execBtn = $("<input>", {
       type:"button",
       id:"execBtn",
       value:"Execute"
    })
    .click(runCommand);

jQuery也将解决所有跨浏览器的问题。


实际上,据我所知,动态创建的内联事件处理程序在使用x.setAttribute()命令创建时确实可以在Internet Explorer 8中完美运行;您只需要在JavaScript代码中正确放置它们即可。我在这里偶然发现了您的问题(和我的问题)的解决方案。

当我将所有包含x.appendChild()的语句移动到正确的位置时(即,紧跟其组中的最后一个setAttribute命令),我发现每个单独的setAttribute都可以在IE8中正常工作,包括所有形式输入属性(包括" name "和" type "属性,以及我的" onclick "事件处理程序)。

我发现这非常出色,因为在执行此操作之前,我在IE中获得的所有内容都是在整个屏幕上呈现垃圾内容,一个又一个错误。另外,我发现每个setAttribute仍然可以在其他浏览器中使用,因此,如果您只记得这种简单的编码实践,则在大多数情况下都可以使用。

但是,如果您必须即时更改任何属性,则此解决方案将不起作用,因为一旦将HTML元素附加到DOM后就无法在IE中对其进行更改;在这种情况下,我可以想象必须从DOM中删除该元素,然后重新创建它及其属性(当然,以正确的顺序!),它们才能正常工作,并且不会引发任何错误。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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+');');
    }
}

内联编写函数,解释器足够聪明,可以知道您正在编写函数。这样做,并假设它只是一个字符串(从技术上讲就是这样)。


在某些情况下,此处列出的示例在Internet Explorer中对我来说不起作用。

因为您必须使用这样的方法设置属性(不带括号)

1
HtmlElement.onclick = myMethod;

如果您必须传递对象名称或什至参数,它将不起作用。对于Internet Explorer,您应该在运行时中创建一个新对象:

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

在其他浏览器上也可以使用。


您尝试过:

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

我这样做是为了解决它并继续前进,在我的情况下,当我尝试为此设置" onclick "属性时,我没有使用\\'input \\'元素,而是使用了图像image我也遇到了同样的问题,所以我尝试用" a "元素package图像,并将引用指向这样的函数。

1
2
3
4
5
6
7
8
9
10
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.webp");
imgDeleter.setAttribute('border',"0");

linkDeleter.appendChild(imgDeleter);

您是否考虑过事件监听器而不是设置属性?除其他外,它使您可以传递参数,这是我在尝试执行此操作时遇到的一个问题。对于IE和Mozilla,您仍然必须做两次:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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");

仅让event为" click "或" mouseover "之类的名称。


与onclick问题无关,但也与以下内容有关:

对于名称与javascript保留字冲突的html属性,请选择一个备用名称,例如。 ,但div.className,或<label for='...'>,但label.htmlFor

在合理的浏览器中,这不会影响setAttribute。因此,在gecko和webkit中,您将调用div.setAttribute('class', 'foo'),但是在IE中,您必须改用javascript属性名称,因此,是div.setAttribute('className', 'foo')


推荐阅读