I was playing around with timers the other day, trying to see how different browsers handled different edge cases, when I stumbled across a fascinatingly bizarre quirk in Internet Explorer. I wouldn’t call it a bug, per se, since that would imply that there was an excepted result, but there is not. The code:
setInterval(function(){ alert("hello!"); }, -1);
Now, before you view the demo (view in IE only!) try to guess what the above code does.
Since -1 isn’t a valid interval my guess was that it would, either (based upon Internet Explorer’s current setInterval behavior):
- Execute immediately, like a normal function, but then never fire again.
- or not execute at all.
What happened is positively bizarre: The callback function will be executed every time the user left clicks the mouse, anywhere in the document.
I find this to be absolutely hilarious. I’m struggling to think of how this could, possibly, be used in any remotely feasible way. Some random ideas:
- A global click handler that can’t be removed and doesn’t leak memory (presumably since no DOM elements involved?)
- Some sort of vector for XSS attacks. I don’t know what the situation would be where you have access to setInterval but not the DOM, so I’m not sure what credence this has.
- A way to initiate a fake event capturing click event (before the actual bubbling event occurs).
I don’t know – I’m not sure if any of them are terribly useful but I find it to be amusing nonetheless. Thoughts on how we can have fun with this?
Pat Cavit (February 16, 2008 at 2:56 am)
Is there any way to figure out what was just clicked on if you were to use it as a global click handler?
I can’t think of many situations where it would be useful to know that a click happened and do something, but not know what was actually clicked on.
Matt McCarthy (February 16, 2008 at 3:26 am)
Drat, it’s the same for other negative numbers. I was hoping that each negative number would correspond to a different event (like if it corresponded to an integer enum used internally).
Noah Aboussafy (February 16, 2008 at 3:26 am)
Since Parallels isn’t working for me right now (and the developer is being totally un-helpful) so I can’t check it out myself but is this for IE 6 or 7 or both?
An interesting thing to test would be to see if the timers normally are effected in some way by right clicking.
Joe Chung (February 16, 2008 at 3:43 am)
It doesn’t happen if you put a DOCTYPE on the page. Weird!
Patrick Wolf (February 16, 2008 at 6:30 am)
*lol* That’s a real wired behavior!
Patrick
Mathias Bynens (February 16, 2008 at 8:14 am)
That’s just hilarious.
Could be useful to indirectly force people to get a better browser, i.e.:
alert('You are using IE! AND YOU JUST CLICKED!!! OMG');
Borgar (February 16, 2008 at 8:28 am)
This doesn’t seem to work for me in IE6 running through CrossOver (it behaves same way FF does), but that may be no surprise.
This may be consistent with (or connected to) the handling of CSS behaviors; behavior code is executed every time the JS thread becomes free. So is this exclusive to left-click with no action on keypress, scroll, or resize?
So you’re saying that this occurs before any event handling is triggered? I’m sure someone can find a use for this, even though it may not be obvious just yet.
Oleg Stepura (February 16, 2008 at 8:51 am)
Mathias Bynens, lol. Best way of using this.
John, don`t put this in the jquery, pls
John Resig (February 16, 2008 at 10:30 am)
@Pat Cavit: Well, the element wouldn’t be immediately available, but maybe there might be some fruit in using elementFromPoint to divine what was just clicked. Dunno!
@Matt McCarthy: Haha – I like your line of thought!
@Noah Aboussafy: It seems to be in both. Rather unsurprising since they didn’t fix any of the JS engine bugs in IE 7. That’s a good point, though, it’s possible that clicks could influence setIntervals. Seem like it would be challenging to test, however.
@Joe Chung: Huh! Good catch! So only in Quirksmode and only with an interval of -1. It gets stranger and stranger.
@Mathias Bynens: Well the tricky problem is that it doesn’t gracefully degrade. In Firefox, for example, it goes into a never-ending loop (equivalent to setInterval(fn,0)).
@Borgar: That’s weird about Crossover – “worked” fine for me in Parallels, not sure what the difference is.
And, yes, it does appear to be exclusive to left-click with no action. And it does appear to happen before an event handler is triggered. Here’s an extra bit of weirdness: If you do an alert in the setInterval callback the document click handler isn’t called, at all. It only seems to happen with an alert but it’s odd, nonetheless.
@Oleg Stepura: Don’t worry bout that ;-)
Eber Irigoyen (February 16, 2008 at 3:19 pm)
is that the result of an overflow?
Lasse R.H. Nielsen (February 16, 2008 at 3:54 pm)
Probably not an overflow.
More likely, the interneal working of timers store the next point in time when it must execute. If that point is reache normally, the event is triggered. If some other code is executing at the time, i.e., another event, then after that code is done, the timers check whether any timers are overdue for triggering, and handles those.
Our timer here will start out with a next trigger time of 1 ms ago. We will not hit that time by waiting, but if we check what events we are overdue for, it will trigger.
I would have expected it to trigger on other things than click events, but it doesn’t seem so. Keyboard events can’t trigger the alert.
/L
Ara Pehlivanian (February 16, 2008 at 6:48 pm)
I wonder if this couldn’t be used to trap the click off of an opened select box when the value isn’t changed in IE6. The box closes when you click on the document, but there is no event anywhere that fires. It would be useful to trap the event when resizing the select box (since IE in general sucks with select box sizing).
Luca (February 17, 2008 at 7:21 am)
Uhm…not working for me. It just blocks IE with alerts… :|
(on WinXP+IE6)
AxiomShell (February 17, 2008 at 7:48 am)
It happens with Safari 3.0.4 (523.12.9) also (on Windows XP SP2)
Fel (February 17, 2008 at 1:27 pm)
IE generally has some weird stuff tied into event loops, perhaps like their evaluation of CSS expressions. Still, it never ceases to amaze me :)
I think speculation on what this would be useful for wont be fruitful. Seems like a straight forward bug to me.
Hamish M (February 17, 2008 at 5:39 pm)
Wow, that’s really weird. Nice find John.
I can’t think of anything really interesting to do with it; other than something sort of prank-ish.
I actually tried viewing the page in Firefox, and it seems like your code makes the alert popup indefinitely, I almost couldn’t close the tab I opened!
ohxten (February 17, 2008 at 6:08 pm)
I love these types of things. :)
At first, before I clicked the link, I thought maybe the reason it popped up when you left-clicked was because it would cause the UI thread to update. But right-clicking doesn’t do anything, and neither does a keyboard event.
What happens if you use some other negative number besides -1?
AxiomShell said it works on Safari on Windows, which makes it even weirder.
Also, does it happen if the alert()-calling function is declared externally, outside the setInterval() function?
Kristoffer E (February 17, 2008 at 10:49 pm)
In Opera 9.25 on Ubuntu 7.10 it immediately gives me a JavaScript info dialog saying
“Triggered with a click, but no click handler”
which seems even more strange since I didn’t even click…
Anyhoo – the dialog keeps coming back after about .5 sec every time I close it.
Just another data point.
/Cheers
Jonathan Tang (February 17, 2008 at 11:30 pm)
It gives you built-in single-stepping for a CPS-ing interpreter written in JavaScript.
If you implement continuations by converting the language to continuation-passing style and then using window.setTimeout(cont, 0) as a trampoline, you can get free tail-recursion in JavaScript. Then if instead of setTimeout(cont, 0) you do setTimeout(cont, debug) and set debug to -1 when the debugger is enabled, it’ll step through each evaluation function when the mouse is clicked, and you can do things like display internal interpreter data structures during evaluation.
Razee Marikar (February 18, 2008 at 1:20 am)
Best discovery about IE ever!!! As for those who said you get indefinite popups in FF / Opera etc, this is what is expected out of a setInterval. That is how it should work. Keep posting em Resig. By the way, in FF (atleast in Netscape) you can close the tab even with these alerts filling up!!!
Razee Marikar (February 18, 2008 at 1:22 am)
Hey, I have something to add… Do the same thing, but with setTimeout instead of setInterval, and you get the popup at the first click only!!! Some wierd stuff
Maxime Haineault (February 18, 2008 at 12:48 pm)
Having fun with Internet Explorer.. is it even possible ? :)
Another weird behavior:
http://reddit.com/info/684il/comments/c034qrr
Tim (February 20, 2008 at 5:33 am)
The only thing I can think of is just fucking with IE users…
davey (February 21, 2008 at 3:00 am)
Damn, I had hoped initially that it enabled you to execute code in the past :-)
potentially a javascript version of the 6 word story by Alan Moore.
‘machine. Unexpectedly I had invented a time’
davey (February 21, 2008 at 3:02 am)
oops.
‘machine. Unexpectedly I invented a time’
Philip Tellis (February 25, 2008 at 6:41 am)
Hmm, I don’t have IE installed anywhere, but perhaps someone else could check if this works inside an iframe, ie, if you set the handler inside an iframe, does it fire on clicks outside? Still may not be able to get anything useful out of it.
Chris Phillips (February 27, 2008 at 12:26 am)
It doesn’t work across iFrames. Although bizarre I don’t really see how this could cause XSS vulnerabilities. I wonder if in IE the single click event does something special that other events don’t do that would require special attention to javascript on a second browser thread. Could it be that the “watch” for single clicks in IE is on its own thread, therefore the need to sync?
Lennie (April 10, 2008 at 7:07 am)
Just to give you an idea how more predictable IE5.5 is in comparison with IE6, etc. And I see this often.
IE5.5sp2 has no problem with this, it just displays the alert many times.