Xpoint
   [напомнить пароль]

Эмуляция selectionStart/selectionEnd в Internet Explorer

Для работы с отмеченной областью текстовых полей в Internet Explorer существует объект selection, предоставляющий широкие возможности. Однако, когда требуется выполнить элементарную операцию, к примеру выделение определённого слова в тексте, вдруг выясняется, что сделать это очень сложно. Хотелось бы, чтобы текстовые поля в Internet Explorer поддерживали свойства selectionStart/selectionEnd и метод setSelectionRange(), реализованные в Gecko-браузерах.

К счастью, с помощью HTC это можно сделать. Но решение получается весьма сложное и содержит целый ряд хаков, чтобы обойти некоторые нюансы реализации selection в Internet Explorer, которые иначе как глюками не назвать. Будущие версии Internet Explorer могут исправить их, тогда решение перестанет работать.

Итак, код файла selection.htc:

<public:component>
  <public:property name="selectionStart" get="getSelectionStart" />
  <public:property name="selectionEnd" get="getSelectionEnd" />
  <public:attach event="onfocus" handler="storeSelection" />
  <public:attach event="onselect" handler="storeSelection" />
  <public:attach event="onclick" handler="storeSelection" />
  <public:attach event="onkeyup" handler="storeSelection" />
  <public:method name="setSelectionRange" />
  <script type="text/javascript">
    var selection = null;
    var selectionStart = -1;
    var selectionEnd = -1;

    function storeSelection(noRecurse)
    {
      // Иногда document.selection устанавливается правильно лишь
      // спустя некоторое время, приходится это учитывать
      if (!noRecurse)
        setTimeout(function() {storeSelection(1)}, 500);

      if (element.document.selection.type != "None" && element.document.selection.type != "Text")
        return;

      var range = element.document.selection.createRange();
      if (range.parentElement() != element)
        return;

      selection = range.duplicate();
    }

    function getSelectionStart()
    {
      updateVars()
      return selectionStart;
    }
    function getSelectionEnd()
    {
      updateVars();
      return selectionEnd;
    }
    function updateVars()
    {
      if (!selection)
        return;

      selectionStart = selectionEnd = -1;

      // Поправка на странный глюк при определении позиции в конце однострочного текста
      if (element.value.indexOf("\r") < 0)
      {
        var wholeRange = element.document.body.createTextRange();
        wholeRange.moveToElementText(element);
        if (!selection.compareEndPoints("startToEnd", wholeRange))
          selectionStart++;
        if (!selection.compareEndPoints("endToEnd", wholeRange))
          selectionEnd++;
      }

      selectionStart -= selection.moveStart("character", -element.value.length);
      selectionEnd -= selection.moveEnd("character", -element.value.length);
      selection = null;

      // Вводим поправку на то, что знак \r не учитывается
      var pos = -1;
      do
      {
        pos = element.value.indexOf("\r", pos + 1);
        if (pos >= 0 && selectionStart > pos)
          selectionStart++;
        if (pos >= 0 && selectionEnd > pos)
          selectionEnd++;
      } while (pos >= 0 && pos < selectionEnd);
    }

    function setSelectionRange(startPos, endPos)
    {
      if (startPos > endPos)
        startPos = endPos;

      // Вводим поправку на то, что знак \r не учитывается
      var startCorrection = element.value.substr(0, startPos).match(/\r/g);
      startCorrection = startCorrection ? startCorrection.length : 0;
      var endCorrection = element.value.substr(0, endPos).match(/\r/g);
      endCorrection = endCorrection ? endCorrection.length : 0;

      selection = element.createTextRange();
      selection.collapse(true);
      selection.moveEnd("character", endPos - endCorrection);
      selection.moveStart("character", startPos - startCorrection);
  
      selection.select();

      // Это нужно для случая, когда startPos == endPos
      storeSelection();
    }
  </script>
</public:component>

Чтобы подключить его к HTML-файлу, в него нужно добавить:

<style type="text/css">
  textarea, input {
    behavior: url(selection.htc);
  }
</style>

Теперь все текстовые поля поддерживают нужные свойства и методы:

<textarea id="test">123456</textarea>

...

<script type="text/javascript">
var text = document.getElementById("test");
text.setSelectionRange(3, 5);  // выберет "45"
alert(text.selectionStart);    // 3
alert(text.selectionEnd);      // 5
alert(text.value.substring(text.selectionStart, text.selectionEnd));  // "45"
</script>

Заметим, что в Internet Explorer перевод строки в текстовом поле представляют два символа (\r\n) в отличие от Gecko-браузеров, где используется только \n. Значения, которые возвращают свойства selectionStart и selectionEnd, это учитывают, чтобы их можно было непосредственно передать в substring().

Powered by POEM™ Engine Copyright © 2002-2005