IE TextRange метод выбора не работает должным образом - PullRequest
7 голосов
/ 25 сентября 2008

У меня необычная проблема с документом IE, в котором для contentEditable задано значение true. Вызов select () для диапазона, который расположен в конце текстового узла, который непосредственно предшествует элементу блока, вызывает смещение выделения на один символ вправо и появляется там, где это не должно быть. Я отправил ошибку в Microsoft против IE8. Если вы можете, пожалуйста, проголосуйте за эту проблему, чтобы исправить ее.

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

Я написал тестовый пример, чтобы продемонстрировать эффект:

<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's 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>

Проблема раскрыта в функции myhandler. Это все, что я делаю, нет шага 3 между сохранением и восстановлением выделения, и все же курсор перемещается. Похоже, что это не произойдет, если выделение не будет пустым (т. Е. У меня есть мигающий курсор, но нет текста), и это происходит только тогда, когда курсор находится в конце текстового узла, который непосредственно предшествует блочному узлу.

Кажется, что диапазон все еще находится в правильной позиции (если я вызываю parentElement для диапазона, он возвращает div), но если я получаю новый диапазон из текущего выделения, новый диапазон находится внутри тега абзаца, и это его родительский элемент.

Как мне обойти это и последовательно сохранять и восстанавливать выделение в Internet Explorer?

Ответы [ 4 ]

12 голосов
/ 06 мая 2009

Я нашел несколько методов для работы с диапазонами IE, как это.

Если все, что вы хотите сделать, это сохранить там, где находится курсор, а затем восстановить его, вы можете использовать метод pasteHTML для вставки пустого диапазона в текущую позицию курсора, а затем использовать метод moveToElementText для его возврата снова в этой позиции:

// 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);

Кроме того, вы можете подсчитать, сколько символов предшествует текущей позиции курсора, и сохранить это число:

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

Чтобы восстановить курсор, установите его в начало, а затем переместите на это количество символов:

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

Надеюсь, это поможет.

1 голос
/ 05 мая 2009

Я немного покопался и, к сожалению, не вижу обходного пути ... Хотя одна вещь, которую я заметил при отладке javascript, похоже, что проблема на самом деле связана с самим объектом диапазона, а не с последующим range.select(). Если вы посмотрите на значения объекта диапазона, которые возвращает selection.createRange(), то да, родительский объект dom может быть верным, но информация о позиционировании уже ссылается на начало следующей строки (т.е. offsetLeft / Top, boundingLeft / Top и т. Д. уже ошиблись).

Основываясь на информации здесь , здесь и здесь , я думаю, вам не повезло с IE, поскольку у вас есть доступ только к TextRange от Microsoft объект, который кажется сломанным. Из моих экспериментов вы можете переместить диапазон вокруг и расположить его точно так, как он должен быть, но как только вы это сделаете, объект диапазона автоматически сместится вниз на следующую строку, даже прежде чем вы попытаетесь .select() его. Например, вы можете увидеть проблему, поместив этот код между вашими шагами 1 и 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
    //(i.e. 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);
}

Так что, к сожалению, нет никакого способа убедиться, что диапазон находится в правильном месте при повторном выборе.

Очевидно, есть тривиальное исправление вашего кода выше, изменив Шаг 2 следующим образом:

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

Но, по-видимому, вы действительно хотите сделать что-то между Шагом 1 и Шагом 2 в своем действительном процессе, что включает в себя перемещение выделения, отсюда необходимость его переустановки. Но на всякий случай. :)

Итак, лучшее, что я могу сделать, - это пойти и проголосовать за ошибку, которую вы создали ... Надеюсь, они это исправят.

0 голосов
/ 06 мая 2009

Может быть, я неправильно понимаю, но если я щелкну справа от этой первой строки, мой курсор уже сразу появится в начале второй строки, так что это не проблема TextRange, верно?

Добавление типа документа, чтобы тестовая страница отображалась в стандартном режиме вместо режима причуд, исправляет это для меня.

И я не могу воспроизвести то, что делает ваша функция myhandler, потому что нажатие на эту кнопку смещает фокус на кнопку, поэтому я больше не вижу курсор и не могу вернуть его обратно. Поиск позиции курсора в contentEditable область , кажется, совсем другая проблема .

0 голосов
/ 29 сентября 2008

Я недавно работал на сайте, который использовал Microsoft CMS с «пакетом MSIB +» элементов управления, который включал редактор WYSIWYG, который работал в Internet Explorer.

Кажется, я помню некоторые комментарии в редакторе Javascript на стороне клиента, которые были конкретно связаны с этой ошибкой в ​​IE и методом Range.Select ().

К сожалению, я там больше не работаю, поэтому не могу получить доступ к файлам Javascript, но, возможно, вы сможете получить их из других мест?

Удачи

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...