User:Catrope/Bizarre browser bugs

Opera
Opera does a couple of annoying brain-damaged things that makes web developers go crazy. If someone ever starts serial-killing Opera developers, here's why:

Newline handling
The representation of newlines varies over browsers. For instance, Firefox represents newlines as  (even on Windows), while IE represents them as. Both browsers seem to be tolerant when trying to insert text in the 'wrong' newline format, and automatically convert it. This means you can code JS quite nicely without having to worry about one browser saying a newline is one character while the other says it's two.

Since Opera is this schizophrenic browser torn between wanting to be a good browser and wanting to be like IE, it came up with the following compromise: when you grab the contents of a textarea using  or  , newlines are represented as   , i.e. as one character. However, when manipulating selections using  or   (Opera, in its schizophrenia, supports both the Gecko way and the IE way), newlines are treated as two characters (presumably  ). This means that the following snippet doesn't do what you expect in Opera: In Firefox, this would search for the string "foo" in the textarea and select it if found. It'll also work in IE if you replace the  assignment with something using a TextRange object. However, it won't work in Opera, because of the different newline representations: the selection will be off to the left by the number of newlines preceding the found occurrence of "foo".

To hack around this, you'd have to do  and then call. Of course, you only want to do this when you're sure you're on a broken version of Opera (to my knowledge, all current versions are broken, but they might fix it some day), so you need a function that tests for this brokenness, then conditionally replaces  with.

The code for this function can be found in this file in a function called.

Moving selections
Some older versions of Opera (I only tested this on 9.0, not sure about 9.4 or 10) are very picky about how selections are moved. A typical code snipper to select the 4th through the 8th character would be: This looks very intuitive and works fine... initially. However, changing the selection to  afterwards using the same template doesn't do what you want: you end up selecting.

The reason for this is that in between the  and the   assignments, the selection would be  ; that is,   would be moved past. All sane browsers that use  allow this and treat negative-length selections as selections of length zero; that is, if   the cursor is placed at   and nothing is selected. However, Opera is not flexible like that: it stubbornly refuses to set  past , and simply ignores the assignment. To work around that, you have to set  first so   can safely be moved. However, assuming that this bug is symmetrical, moving  first is actually right when moving the selection back, so as to prevent moving   back past. This results in the following code:

However, even this doesn't fully address Opera 9.0's bugginess. In addition to refusing to move selections 'the wrong way around', it also refuses to create a zero-length selection before the current cursor position, that is, move the cursor back without selecting anything. In detail, it refuses to change  to be equal to. The reverse seems to work, but that's only an option when moving forward.

Internet Explorer
Some of the bugs and workarounds in this section were found by or in cooperation with Trevor Parscal.

Content-editable iframe manipulation
IE has some weird bugs related to content-editable iframes. They're detailed below in increasing order of weirdness.

A simple attempt at getting a content-editable iframe to work:

This works nicely in Firefox, but in IE the document.open fails, because the iframe hasn't been properly initialized yet. It's been added to the DOM, but it hasn't recevied its own DOM yet. This seems to happen semi-asynchronously: either the iframe is being constructed in parallel, or IE merely schedules the iframe to be constructed after the current event has been processed (I think the latter is more likely).

To work around this, we put the rest of the code in a setTimeout. Timeouts also fire after the current event has been processed completely, and in the case of IE it also seems to fire after the async construction of the iframe. This even happens for timeout intervals of 0 ms. Of course all of this is balancing on implementation details only barely coming together, but it works:

With this workaround, the document.* function calls don't fail any more. Instead, the iframe shows up for a short while, and then the entire page becomes blank. What's happening here is the most bizarre, confusing and scary phenomenon I've ever encountered. For some reason, IE doesn't like the fact that we're messing with the body element inside the iframe (the bug doesn't appear when messing with a pre inside the body, and also appears when using .text instead of .html), and just totally freaks out. It runs through the room in circles, screaming, with its hands against its head. And when it's calmed down, it finds out that it's decapitated the DOM.

That's right, the DOM has become headless. For some reason,  and   (point to the html and body elements of the document respectively) have both become null. The DOM elements themselves can find see each other with .parentNode and jQuery's .parent and what not, and you can still access DOM elements if you already had references to them, but traversing the DOM tree from the root down is no longer possible. This means that IE is unable to render anything (that's why the screen goes blank), and that every reference to a property of  or   results in a JS error. Even simple jQuery methods (at least all selector-based ones, it seems) use these, as well as jQuery's event handling system. In short: you thought you were having some iframe fun, and all of a sudden you get a mysterious blank page and even more mysterious JS errors all over the place in jQuery's gore internals, all just because you removed a pre tag.

One workaround for this is to avoid manipulating the directly:

Another one is to load the iframe contents from a .html file and use the load event on the iframe. However, this event fires twice in IE, and only the second occurrence is legit. Once the load event has fired for the second time, manipulating the body is safe, though:

Loss of selection in content-editable iframe
When a content-editable iframe loses focus to an element in the main document, IE forgets the selection and cursor position (which is really a selection of length 0) in the iframe. When focus is restored to the iframe, the cursor will be at the beginning of the iframe. This makes implementing a toolbar that inserts text into the iframe challenging: if you're not careful, clicking a toolbar button will cause said button to gain focus, causing IE to forget where the cursor was and to insert text at the beginning of the iframe contents. To make matters fun, certain natural choices for toolbar buttons cause the aforementioned breakage on IE, while others don't.

The most natural choices for toolbar buttons are: Each of these would have an onclick handler that manipulates the selected text in the iframe. Now let's see which of these leave the iframe selection alone: If you find yourself designing a toolbar button/widget that needs more flexibility than a plain  or   can provide, you can still use a   as long as you take care to put the actual clickable part in an   or.
 * an  if your button is an image
 * an  if it's text
 * a  if you need more flexibility
 * an  works just fine
 * an  works too (remember to return false from the onclick handler)
 * an  without an href attribute does not work
 * a  also doesn't work

Whitespace handling inside dynamically generated tags
Whitespace collapsing is very serious business in IE. When inserting arbitrary HTML into whitespace-sensitive tags (most notably ), you have to be very careful: IE doesn't always realize the tag you're inserting into is whitespace-sensitive, in which case it'll wrongly collapse all sequences of whitespace characters into single spaces. Interestingly, there seems to be a difference between injecting HTML with  and injecting it into the string fed to the jQuery constructor; I guess the latter makes the browser parse the injected HTML 'in context', realize it's inside a   and handle whitespace accordingly.

Newline insertion in normalized HTML in IE 7
After inserting stuff into a content-editable iframe and looking at the resulting HTML, different browsers produce different results: The difference between Firefox and IE 8 is that IE 8 capitalizes tag names (fair enough), but IE 7 additionally inserts newlines here and there. Closer investigation seems to indicate this happens before the start of each block-level element.

Of course each of these three normalized formats is equally valid, but most code developed on Firefox or IE 8 will be confused by IE 7 making up newlines out of nowhere, especially if said code is something like.