如何在IE8中触发自定义JavaScript事件?

11

我正在尝试在IE8上触发自定义事件,并从这里这里中找到解决方案。但我无法使其工作...

我正在使用jquery mobile与requireJS和google analytics。因此,我正在跟踪JQM pageshow事件。然而,由于requireJS异步加载脚本,我的绑定需要在javascript“包装器”中进行,否则它会产生错误,因为在解析片段时,既没有加载jquery也没有加载jquery mobile。

所以我在每个页面的末尾包含了这个:

if (document.addEventListener) {
    document.addEventListener("jqmReady",function(){trigAnalytics("jqmReady");alert("FF detected")},false); 
} else if ( document.attachEvent ) {
    document.attachEvent("jqmReady", function(){trigAnalytics("jqmReady");alert("IE detected")}); 
}

当检测到时,我将使用pageshow绑定触发我的分析代码片段:
var trigAnalytics = function( trigger ){ 
    $(document).on('pageshow','div:jqmData(role="page").basePage', function (event, ui) {
        var url = location.href; 
        try { 
            hash = location.hash; 
            if (hash && hash.length > 1) {
                 _gaq.push(['_trackPageview', hash.substr(1)]);
                 _gaq.push(['_setCustomVar', 1, 'id_external', ########, 1 ]);
            } else {
                _gaq.push(['_trackPageview', url]);
                _gaq.push(['_setCustomVar', 1, 'id_external', ########, , 1 ]);
                _gaq.push(['b._trackPageview', url]);
            } 
        } catch(err) { } 
    }); 
    if (typeof _gaq !== "undefined" && _gaq !== null) { 
        $(document).ajaxSend(function(event, xhr, settings){ 
            _gaq.push(['_trackPageview', settings.url]); 
            _gaq.push(['b._trackPageview', settings.url]);
        }); 
    } 
}; 

为了启动事件链,当JQM准备就绪时,我需要触发jqmReady。 JQM使用他们的mobileinit事件来指示这一点。因此,在我的应用程序控制器初始化中,我像这样绑定它:

$(document).bind("mobileinit", function () {

    // non-IE OK
    if (document.createEvent) {
        evt = document.createEvent("Event");
        evt.initEvent("jqmReady", true, true);
        document.dispatchEvent(evt);

    } else if (document.createEventObject) { 
    // MSIE (NOT WORKING)

        document.documentElement.evt = 0; // an expando property
        document.documentElement.attachEvent("jqmReady", function () {
            document.documentElement.evt = document.documentElement.evt + 1;
            });
    }
});

我尝试了仅触发 $(window).trigger('jqmReady'),因为当 mobileinit 触发时,jquery 可用。但是似乎由 addEventListener 创建的事件不能像这样触发,因此我需要一个仅使用 JavaScript 的解决方案来在 IE 中触发自定义事件。
问题: 有人能给我指点如何正确地在 IE8 中触发 JavaScript 自定义事件吗?

因为当此代码运行时,jquery ~ $(document).ready() 将不可用。该片段位于 requireJS 初始化之后的页面上。因此,(1) 页面被解析,(2) requireJS 触发,(3) 我的片段执行。当它执行时,jqueryjquery-mobile 都尚未完全加载。因此,必须仅使用 JavaScript。 - frequent
我不确定我理解你的问题(因此是评论,而不是答案),但dispatchEvent()是否相关? - David Thomas
哪部分不清楚?底线是我需要找到一种只在IE8上可以工作的javascript中触发自定义事件的方法。让我检查dispatchEvent - frequent
@frequent,啊哈,jQuery Mobile.... http://jquerymobile.com/test/docs/api/events.html 重要提示:请使用$(document).bind('pageinit'),而不是$(document).ready() - zb'
@eicto: :-) (以及4个字符) - frequent
显示剩余2条评论
2个回答

12

好的,我终于明白了... 这是它的工作原理:

1)在加载页面时设置jqmReady的监听器

// non-IE: just create a listener for the custom event "jqmReady"
if (document.addEventListener) {
    document.addEventListener("jqmReady",function(){trigAnalytics("jqmReady");alert("FF detected")},false); 
// IE8
} else if ( document.attachEvent ) {

    // create a custom property name jqmReady and set it to 0
    document.documentElement.jqmReady = 0;
    // since IE8 does not allow to listen to custom events, 
    // just listen to onpropertychange
    document.documentElement.attachEvent("onpropertychange", function(event) {

        // if the property changed is the custom jqmReady property
        if (event.propertyName == "jqmReady") {
            trigAnalytics("jqmReady");
            alert("gotcha")
            // remove listener, since it's only used once
            document.documentElement.detachEvent("onpropertychange", arguments.callee);
        }
    });
}

在IE8中,我不监听自定义的jqmReady事件,而是监听onpropertychange事件来监听我的自定义属性jqmReady

2)然后在mobileinit事件中,我会这样触发:

 // non-IE
 if (document.createEvent) {
      evt = document.createEvent("Event");
      evt.initEvent("jqmReady", true, true);
      document.dispatchEvent(evt);
 } else if (document.createEventObject) { // MSIE
      // just change the property 
      // this will trigger onpropertychange
      document.documentElement.jqmReady++;
 };

好主意(鸣谢http://dean.edwards.name/weblog/2009/03/callbacks-vs-events/),也许其他人能够找到它的用途。


2
onpropertychange 的一个缺点是它不能冒泡。您必须事先知道哪个元素的属性已更改。更多信息请参见 http://msdn.microsoft.com/en-us/library/ie/ms536956%28v=vs.85%29.aspx。 - dotnetCarpenter

6

如果有其他人感兴趣,我已将此代码封装到一个静态JavaScript对象中。

function Event () {
}

Event.listen = function (eventName, callback) {
    if(document.addEventListener) {
        document.addEventListener(eventName, callback, false);
    } else {    
        document.documentElement.attachEvent('onpropertychange', function (e) {
            if(e.propertyName  == eventName) {
                callback();
            }            
        });
    }
}

Event.trigger = function (eventName) {
    if(document.createEvent) {
        var event = document.createEvent('Event');
        event.initEvent(eventName, true, true);
        document.dispatchEvent(event);
    } else {
        document.documentElement[eventName]++;
    }
}

使用方法:

Event.listen('myevent', function () {
    alert('myevent triggered!');
});

Event.trigger('myevent');

Demo: http://jsfiddle.net/c5CuF/


很好。您可以添加一个.remove方法来正确处理自定义事件的释放。 - frequent
2
你应该在你的gist中澄清第34行的操作,以消除eval语句。document.documentElement[eventName]++ - dotnetCarpenter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接