shikhalev.org

Маленький рецептик — записать, чтоб не забыть, да и вдруг кому пригодится. Когда мы используем в HTML атрибут contenteditable, может возникнуть желание (и почти наверняка возникнет) сохранять выделение редактируемого текста при кликах снаружи (например, на какие-нибудь кнопки). Сделать это можно например так:

var saved = null;

function storeSelection(e) {
  var main = document.getElementById('main');
  var current = e.target;
  var compare = main.compareDocumentPosition(current);
  if (compare != 0 && (compare & 16) == 0) {
    var range = window.getSelection().getRangeAt(0);
    saved = {
      startContainer : range.startContainer,
      startOffset : range.startOffset,
      endContainer : range.endContainer,
      endOffset : range.endOffset
    };
  }
}

function restoreSelection(e) {
  var main = document.getElementById('main');
  var current = e.target;
  var compare = main.compareDocumentPosition(current);
  if (saved && compare != 0 && (compare & 16) == 0) {
    var range = document.createRange();
    range.setStart(saved.startContainer, saved.startOffset);
    range.setEnd(saved.endContainer, saved.endOffset);
    var selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);
  }
}

window.addEventListener('load', function(e) {
  var body = document.body;
  body.addEventListener('mousedown', storeSelection, false);
  body.addEventListener('mouseup', restoreSelection, false);
}, false);

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

В принципе, механизм очевиден, пояснения по методу compareDocumentPosition() можно найти в документации Mozilla, или на JavaScript.ru.

В процессе довелось столкнуться с интересным моментом — при абсолютном позиционировании элементов на странице могут быть области ничем не покрытые, в т.ч. и элементом body. Соответственно, обработчик события на них не срабатывает, и выделение сбрасывается. Чтобы этого не происходило, я использовал CSS:

body {
  position : absolute;
  top : 0px;
  bottom : 0px;
  left : 0px;
  right : 0px;
  padding : 0px;
  margin : 0px;
}

Демо-пример целиком можно взять по адресу: https://gist.github.com/shikhalev/6246433.