views:

1108

answers:

2

I'm working on implementing sprited buttons in Stackoverflow's beloved WMD markdown editor and I've run into an odd bug. On all versions of IE, the selected text is lost upon button clicks, so, say, highlighting a block of text and clicking the code button acts like you placed the cursor at the end of the selection and clicked the button.

e.g. highlighting this:

This
Is
Code

and clicking the code button give you:

This
Is
Code`enter code here`

What's really weird is that I left the original non-sprited button bar in and that works just fine. In fact ALL buttons and keyboard shortcuts code use the same doClick(button) function!

  • Old-style non-sprited buttons: OK
  • Keyboard shortcuts: OK
  • Sprited buttons in non-IE browsers: OK
  • Sprited buttons in IE: WTF

I've isolated the problem down to a call to selection.createRange() which finds nothing only when the sprited button is clicked. I've tried screwing around with focus()ing and making sure as little as possible happens before the doClick() but no joy. The keyboard shortcuts seem to work because the focus is never lost from the input textarea. Can anyone think of a hack that will let me somehow collect the selected text in IE?

The onclick handler looks like this:

button.onmouseout = function(){
  this.style.backgroundPosition = this.XShift + " " + normalYShift;
};

button.onclick = function() {
  if (this.onmouseout) {
    this.onmouseout();
  }
  doClick(this);
}

I've tried moving the onmouseout call to after the doClick in case that was causing a loss of focus but that's not the problem.

EDIT:

The only thing that seems to be different is that, in the original button code, you are clicking on an image. In the sprited code, you are clicking on a list item <li> with a background image set. Perhaps it's trying to select the non-existent text in my list item?

/EDIT

Actual code is located in my wmd repository on git in the button-cleanup branch.

If you revert to the 0d6d1b32bb42a6bd1d4ac4e409a19fdfe8f1ffcc commit you can see both button bars. The top one is sprited and exhibits the weird behavior. The bottom one contains the remnants of the original button bar and works fine. The suspect code is in the setInputAreaSelectionStartEnd() function in the TextareaState object.

One last thing I should mention is that, for the time being, I'm trying to keep the control in pure Javascript so I'd like to avoid fixing this with an external library like jQuery if that's possible.

Thanks for your help!

A: 

You have to blur() a button before IE can select anything else on a page.

Can you provide a minimal example (only containing relevant code) which reproduces the bug?

Christoph
There are no calls to blur() in the code, old or new. I'll check that out, though. IE isn't actually creating the selection if that makes a difference - it's just reading in the user's selection.
Dana Robinson
Also, you can download the code at github in a zip or tarball. There's a sample HTML file inside containing an embedded WMD editor.
Dana Robinson
@Dana: Okay - I'm currently busy, but I'll check it out later. I had a similar problem setting a selection in the onclick handler for a button - adding a `this.blur()` before the selection code fixed it...
Christoph
+4  A: 

I know what the answer to my own question is.

The sprited buttons are implemented using an HTML list and CSS, where all the list items have a background image. The background image is moved around using CSS to show different buttons and states (like mouseover highlights). Standard CSS button spriting stuff.

This works fine in IE with one exception: IE tries to select the empty list text when you click on the background image "button". The selection in the input textarea goes away and the current selection (which will be returned by document.selection.createRange()) is moved to the empty text in the list item.

The fix for this is simple - I created a variable to cache the selection and a flag. In IE I cache the selection and set the flag in a mousedown event handler. In the text processing, I check for the presence of the flag - if it's set I use the cached range instead of querying document.selection.createRange().

Here are some code snippets:

wmd.ieCachedRange = null;
wmd.ieRetardedClick = false;

if(global.isIE) {
  button.onmousedown =  function() { 
    wmd.ieRetardedClick = true;
    wmd.ieCachedRange = document.selection.createRange(); 
  };
}


var range;
if(wmd.ieRetardedClick && wmd.ieCachedRange) {
  range = wmd.ieCachedRange;
  wmd.ieRetardedClick = false;
}
else {
  range = doc.selection.createRange();
}

The solution is only a few lines of code and avoids messing around with the DOM and potentially creating layout engine issues.

Thanks for your help, Cristoph. I came up with the answer while thinking and googling about your answer.

Dana Robinson
This fixes a whole class of WYSImWYG editor problems, thank you!
Piskvor
This was exactly was I was looking for, thank you!
Dennis