Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ActiveX custom event cannot work on IE11

I upgraded my IE from version 10 to 11 and found that my ActiveX custom event couldn't work.

The reason is that IE11 does not support attachEvent any more, and seems I have to use addEventListener. For example previously I used

obj.attachEvent("onSelected", method1); 

and now, it is

obj.addEventListener("onSelected",method1,false); 

After changing the code, the method1 cannot be triggered. I have no idea how to bind custom event, which is implemented in ActiveX plugin, to JS method and make it work on IE11?

like image 912
yushulx Avatar asked Jul 16 '13 08:07

yushulx


3 Answers

The only way I found so far for IE 11 is using the for...event script blocks:

<script for="myActiveX" event="onSelected(param1, param2)">
  method1(param1, param2);
</script>
<object id="myActiveX" ...></object>

Both elements can also be created dynamically with JavaScript. You only must make sure you set the for attribute with the setAttributemethod:

var handler = document.createElement("script");
handler.setAttribute("for", "myActiveX");
handler.event = "onSelected(param1, param2)";       
handler.appendChild(document.createTextNode("method1(param1, param2);"));
document.body.appendChild(handler);

var activeX = document.createElement("object");
activeX.id = "myActiveX"; 
activeX.codebase = "foobar.cab";
activeX.classid = "CLSID:123456789-1234-1234-1234-123456789012";
document.body.appendChild(activeX);

Older IE versions (IE 8 and older) don't like the above code. For these older browsers you must pass the codebase parameter and for parameter with the createElement method:

var handler = document.createElement('<script for="myActiveX">');
...
var activeX = document.createElement('<object classid="CLSID:123456789-1234-1234-1234-123456789012">');

Newer browsers will throw an exception when encountering this code so to support all IE versions you must catch this exception and then use the other method.

like image 74
kayahr Avatar answered Sep 30 '22 14:09

kayahr


Modifies @Gerrit code to work for anonymous functions, also it simply adds attachEvent functionality back to IE 11 (and polyfill for non ie browsers, although not tested) so the same code can be used. I'm happy I found this page. I was fearful that I would have to do this in VBScript or emulate an older version of IE.

Behavior seems to be the same all the way back to IE5.

Polyfill

if (!window.attachEvent) {
  window.attachEvent = Element.prototype.attachEvent = function (ename, efunction) {
    if (typeof efunction !== 'function') {
      throw new TypeError('Element.prototype.attachEvent - what is trying to be attached is not callable');
    }

    // We got to append somewhere
    var _body = document.getElementsByTagName('body')[0];

    // Get IE Version
    var msie = (function() {
        if (typeof document === "undefined") return false;
        var v = 3, div = document.createElement('div'), a = div.all || [];

        while (div.innerHTML = '<!--[if gt IE '+(++v)+']><br>&lt![endif]-->', a[0]); 

        var _detection = v > 4 ? v : /*@cc_on!@*/false;

        var _version = _detection === true ? 10 : (!(window.ActiveXObject) && "ActiveXObject" in window) === true ? 11 : _detection;

        return _version;

    }());

    // Fix ActiveX not working
    if (msie == 11) {
      var _params = efunction.toString().match(/(\(.*\))\ *{/)[1];
      var _funcName = efunction.toString().match(/^function\s?([^\s(]*)/)[1];
      var _fixAnon = false;

      // Allow for anonymous functions
      if (_funcName == "") {
        _fixAnon = true;
        console.warn('Function name not found. Autogenerating');
        _funcName = "autogenFunction" + Math.floor(Math.random()*9999).toString()
        var _handleFunctionality = "{ var privateFunc = " + efunction.toString() + "; privateFunc.apply(" + _funcName + ", arguments); }";
      }

      // Only one script for|event can be used. Make sure there isn't one already
      var _query = 'script[for=' + this.id + '][event=' + ename + ']';
      var _handle = document.querySelectorAll(_query);
      var _handleFunc = _funcName + _params + ';';

      if (_fixAnon) {
        var _newFuncName = _funcName + _params;
        var _newFunction = "function " + _newFuncName + " " + _handleFunctionality;
        var _newHandle = document.createElement('script');
        _newHandle.type = "text/javascript";
        _newHandle.appendChild(document.createTextNode(_newFunction));
        _body.appendChild(_newHandle);
      }

      // If script for|event exists, reuse it
      if (_handle.length != 0) {
        _handleFunc += _handle[0].textContent;
        _body.removeChild(_handle[0]);
        delete _handle[0];
      }
      _handle = document.createElement('script');
      _handle.setAttribute("for", this.id);
      _handle.event = ename;
      var _handleText = document.createTextNode(_handleFunc);
      _handle.appendChild(document.createTextNode(_handleFunc));
      _body.appendChild(_handle);
    }
    var _event = ename.substr(0,2) == "on" ? ename.substr(2) : ename;

    // Polyfill for non ie browsers
    if (window.addEventListener) this.addEventListener(_event, efunction, false);
  }
}

Example Usage with my ActiveX Object

<script type="text/javascript">
function MyObject_ObjectEvent() {
console.log('I am watching traditionally');
}
var _elem = document.getElementById('MyObject');
_elem.attachEvent('ObjectEvent', function () { console.log('I am watching anonymously') });
_elem.attachEvent('ObjectEvent', MyObject_ObjectEvent);
</script>
like image 30
Verron Knowles Avatar answered Sep 30 '22 13:09

Verron Knowles


Based on kayahr answer I have created a function which does the event registration in IE 11. Thank you very much! It worked for me!

Here is my code (does not work with anonymous functions as _functionCallback parameter):

function AttachIE11Event(obj, _strEventId, _functionCallback) {
        var nameFromToStringRegex = /^function\s?([^\s(]*)/;
        var paramsFromToStringRegex = /\(\)|\(.+\)/;
        var params = _functionCallback.toString().match(paramsFromToStringRegex)[0];
        var functionName = _functionCallback.name || _functionCallback.toString().match(nameFromToStringRegex)[1];
        var handler;
        try {
            handler = document.createElement("script");
            handler.setAttribute("for", obj.id);
        }
        catch(ex) {
            handler = document.createElement('<script for="' + obj.id + '">');
        }
        handler.event = _strEventId + params;
        handler.appendChild(document.createTextNode(functionName + params + ";"));
        document.body.appendChild(handler);
    };

You have to use browser sniffing, and your code has to look like

if(BrowserDetect.browser == "IE" && BrowserDetect.version >= 11)
   AttachIE11Event(obj, "onSelected", method1);
else if(obj.attachEvent)
   obj.attachEvent("onSelected", method1);
else if(obj.addEventListener)
   obj.addEventListener("onSelected", method1);
like image 33
Gerrit Avatar answered Sep 30 '22 12:09

Gerrit