JavaScript事件注册而不使用jQuery

Bab*_*ker 29 javascript jquery

如何在不使用jQuery的情况下正确执行以下操作.

   $(document).ready(function(){
      $("#someButton").click(function(){
        alert("Hello");
      });
   });
Run Code Online (Sandbox Code Playgroud)

谢谢.

CMS*_*CMS 35

最简单的方法是:

// DOM Level 0 way
window.onload = function () {
  document.getElementById("someButton").onclick = function() {
    alert("Hello");
  };
};
Run Code Online (Sandbox Code Playgroud)

这将适用于所有浏览器,但请注意,使用此方法只能将一个事件处理程序附加到事件.

另请注意,onload事件并不完全等同于jQuery的ready事件,onload当页面的所有资源都被完全加载(图像,子帧等等)时将被ready触发,同时在解析DOM后立即触发.

如果要附加多个事件处理程序,可以使用DOM Level 2 Standard element.addEventListerner方法(或element.attachEventIE)

获得跨浏览器绑定事件的最简单的抽象方法是:

function addEvent(el, eventType, handler) {
  if (el.addEventListener) { // DOM Level 2 browsers
    el.addEventListener(eventType, handler, false);
  } else if (el.attachEvent) { // IE <= 8
    el.attachEvent('on' + eventType, handler);
  } else { // ancient browsers
    el['on' + eventType] = handler;
  }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以:

var button = document.getElementById("someButton");
addEvent(button, 'click', function () {
  alert("Hello");
});

addEvent(button, 'click', function () {
  alert("world!");
});
Run Code Online (Sandbox Code Playgroud)

还要注意addEventListener和IE的attachEvent方法有区别,当你将多个处理程序附加到一个事件时,addEventListener它们将以相同的顺序触发,因为处理程序被绑定(FIFO),attachEvent将以相反的顺序触发处理程序(LIFO) ).

点击这里查看示例.


T.J*_*der 8

TL; DR

在现代浏览器上:

<input type="button" id="someButton" value="Some Button">
<script>
document.getElementById("someButton").addEventListener("click", function() {
    alert("Hello");
}, false);
</script>
Run Code Online (Sandbox Code Playgroud)

请注意,脚本位于HTML中的按钮之后.为什么这很重要?继续阅读.

document.getElementById("someButton").addEventListener("click", function() {
    snippet.log("Hello");
}, false);
Run Code Online (Sandbox Code Playgroud)
<input type="button" id="someButton" value="Some Button">
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Run Code Online (Sandbox Code Playgroud)


哦,好,你一直在读.

这有三个部分:

  1. 何时挂钩处理程序(jQuery代码ready用于的部分)

  2. 如何找到要挂钩的元素(jQuery代码$("#someButton")用于的部分)

  3. 如何挂钩处理程序(它click用于的部分)

何时挂钩处理程序

直接在元素上处理事件时,在元素存在之前,您无法挂接事件处理程序.处理页面主HTML中的元素时,一旦解析了HTML,就会知道它们存在.例如:

<div>...</div>
<script>
    // Here, we know that that div exists
</script>
Run Code Online (Sandbox Code Playgroud)

因此,除非您有充分的理由不这样做,否则请将JavaScript代码的脚本标记放在文档的末尾,就在结束</body>标记之前.然后你知道上面(包括document.body)的所有元素都存在.

如果您别无选择,只能将脚本标记放在其他位置(例如将它们放入其中的反模式<head>),那么您必须在解析页面内容时使用事件处理程序.标准的是DOMContentLoaded你挂钩的document.不幸的是,IE8及更早版本不支持它,这是jQuery提供ready回调的部分原因.您也可以loadwindow对象上使用该事件,但是在解析页面元素之后很久才会触发该事件,因为它会在触发之前等待所有外部资源加载(所有图像等).

如何找到要挂钩的元素

在您的示例中,元素具有id值,因此您可以使用document.getElementById它来获取它; 返回对元素对象的引用,或者null如果没有那个引用id.

现代浏览器以及IE8支持document.querySelector,它将找到匹配任何CSS选择器的第一个元素.因此,例如,document.querySelector('div')找到div文档中的第一个,如果有的话(null如果没有).document.querySelector('div.foo table tr.bar')找到的第一个tr与类元素bar这是内部的table,这是一个内div有类元素foo.

现代浏览器(和IE8)也提供document.querySelectorAll,它为您提供与CSS选择器匹配的所有元素的列表(集合).

querySelector并且querySelectorAll也可以提供元素; 当你在元素上使用它们时,它们只会查看该元素的后代.

还有其他各种功能,例如getElementsByTagName(几乎普遍支持,甚至在旧的IE中),getElementsByName(使用name属性;我不知道它有多广泛支持),getElementsByClassName(通过单个CSS类查找; IE8和更早版本'有它),还有其他一些.

如何挂钩处理程序

现代浏览器,包括IE9及更高标准模式,支持addEventListener.使用IE8及更早版本,您必须使用Microsoft的原始版本attachEvent(之前的版本addEventListener).

theElement.addEventListener("click", function() {
    // ...your handler code here
}, false);

// or

theElement.attachEvent("onclick", function() {
    // ...your handler code here
});
Run Code Online (Sandbox Code Playgroud)

请注意,addEventListener只使用事件名称("click"),但attachEvent使用"on"加上事件名称("onclick").还要注意,它addEventListener有一个你几乎总要设置的第三个参数false.(最近的浏览器使参数可选,但并非总是如此,因此,如果您需要支持旧版本,请将其包括在内.)

使用addEventListener或者attachEvent这样做的主要优点是可以很好地与其他人一起使用,因为在使用这些方法时,元素可以为同一事件注册多个事件处理程序.

元素还公开可以为其分配函数的属性,其中属性名称on后跟事件名称,例如onclick:

theElement.onclick = function() {
    // Your handler code
};
Run Code Online (Sandbox Code Playgroud)

这通常是跨浏览器支持的,但是存在一个问题,即元素只能以这种方式连接一个处理程序.

所以,我们可以像这样实现你的jQuery代码:

<script>
document.getElementById("someButton").onclick = function() {
    alert("Hello");
};
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

(注意标签的位置.)但这与其他人不能很好地协调.

document.getElementById("someButton").onclick = function() {
    snippet.log("Hello");
};
Run Code Online (Sandbox Code Playgroud)
<input type="button" id="someButton" value="Some Button">
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Run Code Online (Sandbox Code Playgroud)

或者在现代浏览器上:

<script>
document.getElementById("someButton").addEventListener("click", function() {
    alert("Hello");
}, false);
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

document.getElementById("someButton").addEventListener("click", function() {
    snippet.log("Hello");
}, false);
Run Code Online (Sandbox Code Playgroud)
<input type="button" id="someButton" value="Some Button">
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Run Code Online (Sandbox Code Playgroud)

或者在IE8及更早版本:

<script>
document.getElementById("someButton").attachEvent("onclick", function() {
    alert("Hello");
});
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

document.getElementById("someButton").attachEvent("onclick", function() {
    snippet.log("Hello");
});
Run Code Online (Sandbox Code Playgroud)
<input type="button" id="someButton" value="Some Button">
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Run Code Online (Sandbox Code Playgroud)

现在,假设我们使用querySelectorAll或类似并获取元素列表,而不是仅仅一个元素?我们如何在每个上面挂钩处理程序?

我们可以使用其他答案中的技术来循环遍历集合,因为它将是一个类似于数组的对象(但不是实际的数组).假设我们想要div在类的所有元素上查找处理程序foo:

var list = document.querySelectorAll("div.foo");
Array.prototype.forEach.call(list, function(element) {
    element.addEventListener("click", handler, false);
});
function handler() {
    this.innerHTML = "You clicked me";
}
Run Code Online (Sandbox Code Playgroud)

(不要担心this,我在下面的"更多探索"中介绍.)

var list = document.querySelectorAll("div.foo");
Array.prototype.forEach.call(list, function(element) {
    element.addEventListener("click", handler, false);
});
function handler() {
    this.innerHTML = "You clicked me";
}
Run Code Online (Sandbox Code Playgroud)
<div class="foo">foo</div>
<div class="foo">foo</div>
<div class="foo">foo</div>
<div class="foo">foo</div>
<div class="bar">bar (no handler)</div>
<div class="bar">bar (no handler)</div>
<div class="bar">bar (no handler)</div>
<div class="bar">bar (no handler)</div>
Run Code Online (Sandbox Code Playgroud)

更多探索

事件处理不仅仅是关于获取事件.事件处理程序有时还有其他四件事要做:

  1. 获取有关该事件的信息(例如,如果按下某个键,键是什么键?)

  2. 阻止事件的默认操作(如果有)(例如,防止submit表单上的事件中的默认操作阻止它被提交)

  3. 停止将事件传播(冒泡)到容器元素

  4. 引用事件被挂钩的元素(因此我们可以在多个元素上使用相同的处理程序)

#4很简单:在附加了我们上面讨论过的任何机制的处理程序中,在处理程序中,this将引用该事件被挂钩的元素.(HTML属性连接不是这样,例如<div onclick="handler()">,这是不使用它们的众多原因之一.)如果你已经做了其他事情this(你可以),你也可以访问你挂钩事件的元素通过currentTarget事件对象的属性(更多下面).

不幸的是,你这样做的方式在IE8和更早版本的attachEvent标准兼容浏览器上是不同的(addEventListener).这是一个函数,hookEvent(我最初为其他答案写的),即使在旧的IE上也可以使用基于标准的方式:

var hookEvent = (function() {
    var div;

    // The function we use on standard-compliant browsers
    function standardHookEvent(element, eventName, handler) {
        element.addEventListener(eventName, handler, false);
        return element;
    }

    // The function we use on browsers with the previous Microsoft-specific mechanism
    function oldIEHookEvent(element, eventName, handler) {
        element.attachEvent("on" + eventName, function(e) {
            e = e || window.event;
            e.preventDefault = oldIEPreventDefault;
            e.stopPropagation = oldIEStopPropagation;
            handler.call(element, e);
        });
        return element;
    }

    // Polyfill for preventDefault on old IE
    function oldIEPreventDefault() {
        this.returnValue = false;
    }

    // Polyfill for stopPropagation on old IE
    function oldIEStopPropagation() {
        this.cancelBubble = true;
    }

    // Return the appropriate function; we don't rely on document.body
    // here just in case someone wants to use this within the head
    div = document.createElement('div');
    if (div.addEventListener) {
        div = undefined;
        return standardHookEvent;
    }
    if (div.attachEvent) {
        div = undefined;
        return oldIEHookEvent;
    }
    throw "Neither modern event mechanism (addEventListener nor attachEvent) is supported by this browser.";
})();
Run Code Online (Sandbox Code Playgroud)

使用hookEvent,我们可以按照基于标准的方式执行前面列出的三件事:

  1. 我们接收事件对象作为处理函数的第一个参数,这意味着我们可以使用它currentTarget来获取对我们挂钩事件target的元素的引用,或者获取对事件源自的元素的引用,等等

  2. 我们可以通过调用事件对象的preventDefault函数来阻止默认值

  3. 我们可以通过调用事件对象的stopPropagation函数来停止传播

  4. 我们可以使用this(或event.currentTarget)引用我们挂钩事件的元素

hookEvent不是一个彻底的,全能的,全舞蹈的事件连接功能.(一两件事,我从来没有费心去支持联合国与它挂钩的事件.)这是示例代码,主要是.

参考资料

  • HTML5规范现在涵盖了大部分内容,这比HTML要多得多

  • DOM定义了各个方面; 这里的规格列表,当前的一个是DOM4

  • Mozilla开发者网络拥有大量相当准确的参考资料(与其他一些常常存在准确性问题的元网站不同).