在jQuery中常用trigger()方法来完成模拟操作,如可以使用下面的代码来触发id为test按钮的click事件。
$("#test").trigger("click");
也可以直接简化写法click(),来实现同样的效果:
$("test").click();
如何触发自定义事件
//给element绑定hello事件
element.bind("hello",function(){
console.log("hello world!");
});
//触发hello事件
element.trigger("hello");
上面的bind方法还是如之前文章绑定的流程一样
主要讲下trigger方法
trigger: function(event, data, elem, onlyHandlers) {
var i,
cur,
tmp,
bubbleType,
ontype,
handle,
special,
lastElement,
eventPath = [elem || document],
type = hasOwn.call(event, 'type') ? event.type : event,
namespaces = hasOwn.call(event, 'namespace')
? event.namespace.split('.')
: [];
cur = lastElement = tmp = elem = elem || document;
// Don't do events on text and comment nodes
if (elem.nodeType === 3 || elem.nodeType === 8) {
return;
}
// focus/blur morphs to focusin/out; ensure we're not firing them right now
if (rfocusMorph.test(type + jQuery.event.triggered)) {
return;
}
if (type.indexOf('.') > -1) {
// Namespaced trigger; create a regexp to match event type in handle()
namespaces = type.split('.');
type = namespaces.shift();
namespaces.sort();
}
ontype = type.indexOf(':') < 0 && 'on' + type;
// Caller can pass in a jQuery.Event object, Object, or just an event type string
event = event[jQuery.expando]
? event
: new jQuery.Event(type, typeof event === 'object' && event);
// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
event.isTrigger = onlyHandlers ? 2 : 3;
event.namespace = namespaces.join('.');
event.rnamespace = event.namespace
? new RegExp(
'(^|\\.)' + namespaces.join('\\.(?:.*\\.|)') + '(\\.|$)'
)
: null;
// Clean up the event in case it is being reused
event.result = undefined;
if (!event.target) {
event.target = elem;
}
// Clone any incoming data and prepend the event, creating the handler arg list
data = data == null ? [event] : jQuery.makeArray(data, [event]);
// Allow special events to draw outside the lines
special = jQuery.event.special[type] || {};
if (
!onlyHandlers &&
special.trigger &&
special.trigger.apply(elem, data) === false
) {
return;
}
// Determine event propagation path in advance, per W3C events spec (#9951)
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
if (!onlyHandlers && !special.noBubble && !isWindow(elem)) {
bubbleType = special.delegateType || type;
if (!rfocusMorph.test(bubbleType + type)) {
cur = cur.parentNode;
}
for (; cur; cur = cur.parentNode) {
eventPath.push(cur);
tmp = cur;
}
// Only add window if we got to document (e.g., not plain obj or detached DOM)
if (tmp === (elem.ownerDocument || document)) {
eventPath.push(
tmp.defaultView || tmp.parentWindow || window
);
}
}
// Fire handlers on the event path
i = 0;
while ((cur = eventPath[i++]) && !event.isPropagationStopped()) {
lastElement = cur;
event.type = i > 1 ? bubbleType : special.bindType || type;
// 主要是通过这里,获取当前节点缓存的句柄函数,执行传递进来的回调函数
// jQuery handler
handle =
(dataPriv.get(cur, 'events') || {})[event.type] &&
dataPriv.get(cur, 'handle');
if (handle) {
handle.apply(cur, data);
}
// Native handler
handle = ontype && cur[ontype];
if (handle && handle.apply && acceptData(cur)) {
event.result = handle.apply(cur, data);
if (event.result === false) {
event.preventDefault();
}
}
}
event.type = type;
// If nobody prevented the default action, do it now
if (!onlyHandlers && !event.isDefaultPrevented()) {
if (
(!special._default ||
special._default.apply(eventPath.pop(), data) ===
false) &&
acceptData(elem)
) {
// Call a native DOM method on the target with the same name as the event.
// Don't do default actions on window, that's where global variables be (#6170)
if (ontype && isFunction(elem[type]) && !isWindow(elem)) {
// Don't re-trigger an onFOO event when we call its FOO() method
tmp = elem[ontype];
if (tmp) {
elem[ontype] = null;
}
// Prevent re-triggering of the same event, since we already bubbled it above
jQuery.event.triggered = type;
if (event.isPropagationStopped()) {
lastElement.addEventListener(
type,
stopPropagationCallback
);
}
elem[type]();
if (event.isPropagationStopped()) {
lastElement.removeEventListener(
type,
stopPropagationCallback
);
}
jQuery.event.triggered = undefined;
if (tmp) {
elem[ontype] = tmp;
}
}
}
}
return event.result;
},
上面的源码主要做以下的几件事:
1 命名空间的过滤
if (type.indexOf('.') > -1) {
// Namespaced trigger; create a regexp to match event type in handle()
namespaces = type.split('.');
type = namespaces.shift();
namespaces.sort();
}
2 模拟事件对象
event = event[jQuery.expando]
? event
: new jQuery.Event(type, typeof event === 'object' && event);
创建jQuery event
3 模拟事件冒泡
// Determine event propagation path in advance, per W3C events spec (#9951)
// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
if (!onlyHandlers && !special.noBubble && !isWindow(elem)) {
bubbleType = special.delegateType || type;
if (!rfocusMorph.test(bubbleType + type)) {
cur = cur.parentNode;
}
for (; cur; cur = cur.parentNode) {
eventPath.push(cur);
tmp = cur;
}
// Only add window if we got to document (e.g., not plain obj or detached DOM)
if (tmp === (elem.ownerDocument || document)) {
eventPath.push(
tmp.defaultView || tmp.parentWindow || window
);
}
}
通过遍历所有元素节点,排个队列出来
4 处理事件
// Fire handlers on the event path
i = 0;
while ((cur = eventPath[i++]) && !event.isPropagationStopped()) {
lastElement = cur;
event.type = i > 1 ? bubbleType : special.bindType || type;
// 主要是通过这里,获取当前节点缓存的句柄函数,执行传递进来的回调函数
// jQuery handler
handle =
(dataPriv.get(cur, 'events') || {})[event.type] &&
dataPriv.get(cur, 'handle');
if (handle) {
handle.apply(cur, data);
}
// Native handler
handle = ontype && cur[ontype];
if (handle && handle.apply && acceptData(cur)) {
event.result = handle.apply(cur, data);
if (event.result === false) {
event.preventDefault();
}
}
}
当然每个元素上可能有多个事件,所以先确定事件绑定类型是delegateType还是bindType
检测缓存中该元素对应事件中包含事件处理器,有则取出主处理器(jQuery handle)来控制所有分事件处理器 所以最终代码又走到了 handle.apply(cur, data);
trigger 源码分析到此为止。