5

I'm trying to add a method to the Event prototype. In order to call/set preventDefault() or, in IE-speak returnValue = false and -if desired- stopPropagation() / cancelBubble = true;. I thought the code below would have sufficed.

Event = Event || window.Event;
//^^ makes the fiddle work on IE8 ^^
if(!(Event.prototype.stopEvent))
{
    Event.prototype.stopEvent = function(propagate)
    {
        "use strict";
        propagate = (propagate ? true : false);
        if (this.preventDefault)
        {
            this.preventDefault();
            if (propagate === false)
            {
                this.stopPropagation();
            }
        }
        else
        {
            this.returnValue = false;
            this.cancelBubble = !propagate;
        }
        return this;
    };
}

Which seems to work, as you can see here. This fiddle shows OK in IE8, firefox and chrome. Though, when I add this to my script, IE8 breaks on the first line, saying 'Event is undefined'. Leaving out "use strict"; makes no difference at all.

Reluctantly, I tried this, too:

if (typeof Event === 'undefined')
{
    var Event = window.Event || window.event;//FFS IE :-(
}

But to no avail: Error: 'Event.prototype' is null or not an object, so I got 1 line further. The thing is, the entire prototype method is a copy paste from my script, but what am I overlooking here? Any idea's/suggestions?
Thanks

PS: I like Pure JavaScript, so please, don't suggest jQuery, prototypejs, dojo,... as a solution. I've just gotten rid of jQuery. (I like jQuery, but there is no need for it in this case)


Update

Things have taken a turn for the worse, I'm afraid. I found this MSDN reference. The entire page deals with DOM Element prototypes. It's pretty fair to say they are available and usable in IE8 (to some extent). On this page, this code caught my eye:

Event.prototype.stopPropagation = function ()
{
  this.cancelBubble = true;
};
Event.prototype.preventDefault = function ()
{
  this.returnValue = false;
};

It can be found ~3/4ths of the page down, in the section titled "Powerful Scenarios". This is, to my mind exactly the same thing as I want to do, but what's more: if I try this code via jsfiddle, it doesn't even work, whereas my jsfiddle (with my code) did work on IE8. This is just the last few lines of a snippet, but as far as I can work out, these few lines of code should work just fine. I've altered them as follows:

Event.prototype.stopPropagation = function ()
{
    if (this.stopPropagation)
    {
        return this.stopPropagation();
    }
    this.cancelBubble = true;
};
Event.prototype.preventDefault = function ()
{
    if (this.preventDefault)
    {
        return this.preventDefault();
    }
    this.returnValue = false;
};
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • 3
    What you are missing is that host objects are not required to implement any kind of inheritance, much less prototype inheritance. And even if they do, you can't expect to modify them. Also, IE 8 implements some such features but only in standards mode, not quirks mode. So a general solution is ruled out until IE 8 and other browsers that don't implement prototype inheritance for DOM objects are no longer in use. That might be quite a while. – RobG May 16 '12 at 11:02
  • 1
    That still doesn't explain why the same code is working in the fiddle I set up, but not on my page. All browsers work fine there, except for IE8 - which isn't running in quirks mode. – Elias Van Ootegem May 16 '12 at 11:08
  • Does it still work if you access the resulting frame directly? http://fiddle.jshell.net/v4sTx/4/show/ (sorry, I don't have IE8 accessible to me ATM to test myself) – Misha Reyzlin May 18 '12 at 11:07
  • @gryzzly: Yup, it works fine there, too. Even in compatibility mode. I can't work it out at all. I've tried setting up a [second one](http://fiddle.jshell.net/MhpW8/). That, too works in IE8 :-S, even if I access the frame directly. – Elias Van Ootegem May 18 '12 at 11:19
  • My practical suggestion would be to not suffer and to wrap events functionality with your own namespace and delegate the logic there (what jQuery and others do), however, it's very interesting why this happens – Misha Reyzlin May 18 '12 at 11:34
  • Looking at prototype.js, the guys who have been extending native object prototypes for ages and who have been around in JS scene for very long time as well: https://github.com/sstephenson/prototype/blob/master/src/prototype/dom/event.js seems like they don't extend Event.prototype for IE – Misha Reyzlin May 18 '12 at 11:43
  • I have been looking at prototype.js for inspiration - They're working all sorts of voodoo magic on, what they refer to as IELegacyEvents. I'm currently using event delegation to get around the problem, however, I can't stand not knowing why things are working one time, and failing the next moment. – Elias Van Ootegem May 18 '12 at 14:49

2 Answers2

3

I recently had (another) brainwave, and I think I found a better way of augmenting the event prototype. Strictly speaking, the Event prototype is not accessible in IE (<9), but it is, I found out accessible if you work your way back from an instance (well, the instance: window.event). So here's a snippet that works in all major browsers (and IE8 - not 7):

(function()
{
        function ol(e)
        {//we have an event object
            e = e || window.event;
            if (!e.stopEvent)
            {
                if (Object && Object.getPrototypeOf)
                {//get the prototype
                    e = Object.getPrototypeOf(e);
                }
                else
                {//getting a prototype in IE8 is a bit of a faff, this expression works on most objects, though
                 //it's part of my custom .getPrototypeOf method for IE
                    e = this[e.constructor.toString().match(/(function|object)\s+([A-Z][^\s(\]]+)/)[2]].prototype;
                }
                e.stopEvent = function(bubble)
                {//augment it (e references the prototype now
                    bubble = bubble || false;
                    if (this.preventDefault)
                    {
                        this.preventDefault();
                        if (!bubble)
                        {
                            this.stopPropagation();
                        }
                        return this;
                    }
                    this.returnValue = false;
                    this.cancelBubble = !bubble;
                    return this;
                };
            }
            alert(e.stopEvent ? 'ok' : 'nok');//tested, it alerts ok
            if (this.addEventListener)
            {
                this.removeEventListener('load',ol,false);
                return;
            }
            document.attachEvent('onkeypress',function(e)
            {
                e = e || window.event;
                if (e.stopEvent)
                {//another event, each time alerts ok
                    alert('OK!');
                }
            });
            this.detachEvent('onload',ol);
        }
        if (this.addEventListener)
        {
            this.addEventListener('load',ol,false);
        }
        else
        {
            this.attachEvent('onload',ol);
        }
})();

That way, the header doctype doesn't matter all that much: I've tested it using the <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">, and it works in FF, chrome and IE 8, no problems whatsoever. Using <!DOCTYPE html> to be safe, though

Hope this helps someone...

Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
2

Its Standards versus Quirks mode. The JSFiddle page has a DOCTYPE declaration, albeit an incredibly simple one, <!DOCTYPE html>, that kicks the render into Standards mode. Chances are your web page does not have a DOCTYPE which leaves the render in Quirks mode. After adding that simple DOCTYPE to a page I built from your fiddle, it worked for me.

MarkFisher
  • 516
  • 4
  • 15
  • Darn handy methods on Event, BTW, I'm stealing them! :) – MarkFisher May 21 '12 at 13:38
  • My pages did have a doctype, though not the HTML5 notation. The page is being rendered in Standards mode all the same, but somehow IE8 switched back to IE7 compatible mode. Switching to the HTML5 doctype does seem to work, however. I'd sort of assumed they would have kicked IE into quirks mode, rather then Standards... Well spotted, and Thanks – Elias Van Ootegem May 21 '12 at 17:01
  • 1
    Just a quick note for those who come after; you can detect the render mode of the page by checking the `document.compatMode` property. A value of "CSS1Compat" is Standards mode, anything else (including null) isn't. – MarkFisher May 22 '12 at 14:26