IE TextRange method not working properly

I am having an unusual problem with an IE document when contentEditable is set to true. Calling select () in the range that is located at the end of the node text that immediately precedes the block element causes the selection to shift one character to the right and appears where it should not. I sent an error to Microsoft against IE8. If you can, vote for this problem so that it can be fixed.

https://connect.microsoft.com/IE/feedback/ViewFeedback.aspx?FeedbackID=390995

I wrote a test case to demonstrate the effect:

<html> <body> <iframe id="editable"> <html> <body> <div id="test"> Click to the right of this line -&gt; <p id="par">Block Element</p> </div> </body> </html> </iframe> <input id="mytrigger" type="button" value="Then Click here to Save and Restore" /> <script type="text/javascript"> window.onload = function() { var iframe = document.getElementById('editable'); var doc = iframe.contentDocument || iframe.contentWindow.document; // An IFRAME without a source points to a blank document. Here we'll // copy the content we stored in between the IFRAME tags into that // document. It a hack to allow us to use only one HTML file for this // test. doc.body.innerHTML = iframe.textContent || iframe.innerHTML; // Marke the IFRAME as an editable document if (doc.body.contentEditable) { doc.body.contentEditable = true; } else { var mydoc = doc; doc.designMode = 'On'; } // A function to demonstrate the bug. var myhandler = function() { // Step 1 Get the current selection var selection = doc.selection || iframe.contentWindow.getSelection(); var range = selection.createRange ? selection.createRange() : selection.getRangeAt(0); // Step 2 Restore the selection if (range.select) { range.select(); } else { selection.removeAllRanges(); selection.addRange(range); doc.body.focus(); } } // Set up the button to perform the test code. var button = document.getElementById('mytrigger'); if (button.addEventListener) { button.addEventListener('click', myhandler, false); } else { button.attachEvent('onclick', myhandler); } } </script> </body> </html> 

The problem is revealed in the myhandler function. This is all I do, there is no step 3 between saving and restoring the selection, and yet the cursor moves. This does not seem to happen if the selection is empty (i.e. I have a blinking cursor, but no text), and this seems to happen whenever the cursor is at the end of the node text that immediately precedes the node block.

It seems that the range is still in the correct position (if I call parentElement in the range that the div returns), but if I get a new range from the current selection, the new range is inside the paragraph tag and this is its parent element.

How can I get around this and sequentially save and restore the selection in Internet Explorer?

+7
javascript internet-explorer
Sep 24 '08 at 21:50
source share
4 answers

I have developed several methods to work with such ranges of IE.

If all you want to do is save where the cursor is and then restore it, you can use the pasteHTML method to insert an empty space at the current cursor position, and then use the moveToElementText method to return it at that position:

 // Save position of cursor range.pasteHTML('<span id="caret"></span>') ... // Create new cursor and put it in the old position var caretSpan = iframe.contentWindow.document.getElementById("caret"); var selection = iframe.contentWindow.document.selection; newRange = selection.createRange(); newRange.moveToElementText(caretSpan); 

Alternatively, you can calculate how many characters precede the current cursor position and save this number:

 var selection = iframe.contentWindow.document.selection; var range = selection.createRange().duplicate(); range.moveStart('sentence', -1000000); var cursorPosition = range.text.length; 

To restore the cursor, you set it to the beginning and then move it by the number of characters:

 var newRange = selection.createRange(); newRange.move('sentence', -1000000); newRange.move('character', cursorPosition); 

Hope this helps.

+12
May 05 '09 at 10:48 p.m.
source share

I had a little digging and, unfortunately, I do not see a workaround ... Although one thing I noticed when debugging javascript seems to be the problem with the range object itself, and not with the subsequent range.select() . If you look at the values ​​of the range object returned by selection.createRange() , yes, the parent dom object may be correct, but the location information already refers to the beginning of the next line (e.g. offsetLeft / Top, boundingLeft / Top, etc. already wrong).

Based on the information here , here and here , I think you're out of luck with IE, since you only have access to the Microsoft TextRange object, which seems to be broken. From my experiments, you can move the range and position it exactly where it should be, but as soon as you do this, the range object automatically switches to the next line, even if you didn't try .select() it. For example, you can see the problem by putting this code between steps 1 and 2:

 if (range.boundingWidth == 0) { //looks like its already at the start of the next line down... alert('default position: ' + range.offsetLeft + ', ' + range.offsetTop); //lets move the start of the range one character back //(ie select the last char on the line) range.moveStart("character", -1); //now the range looks good (except that its width will be one char); alert('one char back: ' + range.offsetLeft + ', ' + range.offsetTop); //calculate the true end of the line... var left = range.offsetLeft + range.boundingWidth; var top = range.offsetTop; //now we can collapse back down to 0 width range range.collapse(); //the position looks right alert('moving to: ' + left + ', ' + top); //move there. range.moveToPoint(left, top); //oops... on the next line again... stupid IE. alert('moved to: ' + range.offsetLeft + ', ' + range.offsetTop); } 

So, unfortunately, there doesn't seem to be any way to make sure the range is in the right place when you select it again.

Obviously, there is a trivial fix for your code above by modifying Step 2 as follows:

 // Step 2 Restore the selection if (range.select) { if (range.boundingWidth > 0) { range.select(); } } else { selection.removeAllRanges(); selection.addRange(range); doc.body.focus(); } 

But, apparently, you really want to do something between Step 1 and Step 2 in your action, which includes moving the selection, therefore, the need to redial it. But just in case. :)

So, the best I can do is go and vote for your mistake ... I hope they fix it.

+1
May 05 '09 at
source share

I recently worked on a site that used Microsoft CMS with the "MSIB +" package, which included the WYSIWYG editor that launched in Internet Explorer.

It seems that I remember some comments in the editor on the client side of Javascript that were specifically related to this error in IE and the Range.Select () method.

Unfortunately, I no longer work, so I can’t access the Javascript files, but maybe you can get them from other sources?

Good luck.

0
Sep 29 '08 at 15:49
source share

Perhaps I misunderstand, but if I click to the right of this first line, my cursor will immediately appear at the beginning of the second line, so there is no TextRange problem, right?

Adding the doctype type so that the test page is displayed in standard mode, and not in the quirks mode settings, which are for me.

And I cannot reproduce what your myhandler function does, because clicking this button moves the focus to the button, so I can no longer see the cursor and cannot return it. Finding a cursor position in the contentEditable area seems like a completely different issue .

0
May 6 '09 at 10:36
source share



All Articles