Координаты выделенного текста на странице браузера

Мне нужны координаты в пикселях начала выделения текста (где угодно на странице, а не в текстовом поле).

Я попытался использовать координаты курсора, но это не сработало, потому что координаты курсора и начало выделения не всегда совпадают (например, когда пользователь перетаскивает текст).

Надеюсь, у кого-то есть решение!


person Bouke    schedule 27.07.2011    source источник


Ответы (3)


В IE> = 9 и браузерах, отличных от IE (Firefox 4+, браузеры WebKit, выпущенные с начала 2009 года, Opera 11, возможно, ранее), вы можете использовать _ 1_ метод Range. В IE 4–10 вы можете использовать свойства boundingLeft и boundingTop TextRange, которые можно извлечь из выделения. Вот функция, которая будет делать то, что вы хотите в последних версиях браузеров.

Обратите внимание, что есть некоторые ситуации, в которых вы можете ошибочно получить координаты 0, 0, как упоминалось в комментариях @Louis. В этом случае вам придется вернуться к временному решению: временно вставить элемент и получить его позицию.

jsFiddle: http://jsfiddle.net/NFJ9r/132/

Код:

function getSelectionCoords(win) {
    win = win || window;
    var doc = win.document;
    var sel = doc.selection, range, rects, rect;
    var x = 0, y = 0;
    if (sel) {
        if (sel.type != "Control") {
            range = sel.createRange();
            range.collapse(true);
            x = range.boundingLeft;
            y = range.boundingTop;
        }
    } else if (win.getSelection) {
        sel = win.getSelection();
        if (sel.rangeCount) {
            range = sel.getRangeAt(0).cloneRange();
            if (range.getClientRects) {
                range.collapse(true);
                rects = range.getClientRects();
                if (rects.length > 0) {
                    rect = rects[0];
                }
                x = rect.left;
                y = rect.top;
            }
            // Fall back to inserting a temporary element
            if (x == 0 && y == 0) {
                var span = doc.createElement("span");
                if (span.getClientRects) {
                    // Ensure span has dimensions and position by
                    // adding a zero-width space character
                    span.appendChild( doc.createTextNode("\u200b") );
                    range.insertNode(span);
                    rect = span.getClientRects()[0];
                    x = rect.left;
                    y = rect.top;
                    var spanParent = span.parentNode;
                    spanParent.removeChild(span);

                    // Glue any broken text nodes back together
                    spanParent.normalize();
                }
            }
        }
    }
    return { x: x, y: y };
}

ОБНОВЛЕНИЕ

Я отправил ошибку WebKit в результате комментариев, и теперь она исправлена.

https://bugs.webkit.org/show_bug.cgi?id=65324

person Tim Down    schedule 27.07.2011
comment
Отлично работает с выделением одной строки, но когда вы выбираете несколько строк (начиная где-то с середины первой строки), он показывает координаты начала первой строки выделения, а не начала самого выделения .... Я знаю, что это, вероятно, из-за функции getBounding, но есть ли способ изменить это? - person Bouke; 27.07.2011
comment
@Bouke: Ах, справедливо. Я обновил свой ответ, чтобы сделать так, как вы просили, свернув диапазон до одной точки в начале, прежде чем получить ее положение. - person Tim Down; 28.07.2011
comment
Это отлично работает в firefox, но теперь, похоже, это больше не работает в браузерах webkit ... Похоже, это ошибка webkit, возможно, мне нужно использовать ваш старый скрипт в качестве запасного варианта. - person Bouke; 28.07.2011
comment
@Bouke: Ты прав. WebKit getBoundingClientRect() возвращает null для свернутых диапазонов, что бесполезно. Работаю над этим... - person Tim Down; 28.07.2011
comment
Я пробовал этот код. Он всегда возвращается x: 0, y:0, даже если позиция каретки перемещена в firefox 20.0.1. Взгляните на этот аналогичный вопрос, который я недавно задал. Я ищу кросс-браузерное решение, которое работает в IE8 +, Chrome и Firefox. - person Mr_Green; 20.05.2013
comment
@Mr_Green: этот код специфичен для выборок / диапазонов в пределах обычного HTML-содержимого, а не для текстовых полей, как Rangy. Я не верю, что можно создать общее кроссбраузерное решение для получения координат курсора текстового поля без утомительного измерения текста, поэтому я не ответил на ваш другой вопрос. - person Tim Down; 20.05.2013
comment
@TimDown: мы могли бы сделать это с помощью плагина component.io textarea-caret-position - получите (x, y) позиция _1 _ / _ 2_ в текстовой области, кроссбраузерность и без использования jQuery. - person Dan Dascalescu; 17.03.2014
comment
@TimDown Боюсь, здесь проблема не только с текстовыми полями. Запустите эту скрипку в Chrome и щелкните слева (выделено жирным шрифтом), не выделяя текст. Вы получите 0, 0. Его легче воспроизвести в Chrome, но основная проблема заключается в том, что если диапазон нулевой ширины не в текстовом узле, тогда прямоугольник будет иметь 0, 0 координаты. Я не могу воспроизвести его в FF простым щелчком мыши, потому что он предпочитает устанавливать курсор внутри текстовых узлов при щелчках, но это также может произойти в FF, если выбор обрабатывается программно. - person Louis; 28.05.2014
comment
@ Луис: Да, я тоже это видел. Обходной путь - временно вставить элемент и получить его позицию. К ответу добавлю примечание. - person Tim Down; 29.05.2014
comment
@TimDown возникает ошибка, когда курсор находится в пустом div - person Jakobovski; 21.10.2014
comment
Возможно, добавьте комментарий к if (sel), указав, что он нацелен только на какие версии IE? - person Dan Dascalescu; 21.07.2015
comment
@DanDascalescu Этот ответ предшествует существованию IE 11, который первым не поддерживал document.selection, но IE 11 поддерживает Range и Range.getClientRects, поэтому проблем с фактическим кодом быть не должно. Но я обновлю текст. Справедливо по поводу rects[0], это недосмотр. - person Tim Down; 21.07.2015
comment
@TimDown благодарит за это решение. Вы решили все мои проблемы. Я не знаю почему, но он не работает правильно в сложном html в iOS, например, copytaste.com/aw8146 - person Ankush; 04.10.2016

Приведенный выше ответ TimDown не работает, если курсор находится в пустом элементе.

Приведенный ниже код решает проблему. Обратите внимание, что оно почти идентично решению TimDown, за исключением того, что этот код проверяет, что массив range.getClientRects() имеет length>0 перед вызовом range.getClientRects()[0]

function getSelectionCoords() {
    var sel = document.selection, range, rect;
    var x = 0, y = 0;
    if (sel) {
        if (sel.type != "Control") {
            range = sel.createRange();
            range.collapse(true);
            x = range.boundingLeft;
            y = range.boundingTop;
        }
    } else if (window.getSelection) {
        sel = window.getSelection();
        if (sel.rangeCount) {
            range = sel.getRangeAt(0).cloneRange();
            if (range.getClientRects) {
                range.collapse(true);
                if (range.getClientRects().length>0){
                    rect = range.getClientRects()[0];
                    x = rect.left;
                    y = rect.top;
                }
            }
            // Fall back to inserting a temporary element
            if (x == 0 && y == 0) {
                var span = document.createElement("span");
                if (span.getClientRects) {
                    // Ensure span has dimensions and position by
                    // adding a zero-width space character
                    span.appendChild( document.createTextNode("\u200b") );
                    range.insertNode(span);
                    rect = span.getClientRects()[0];
                    x = rect.left;
                    y = rect.top;
                    var spanParent = span.parentNode;
                    spanParent.removeChild(span);

                    // Glue any broken text nodes back together
                    spanParent.normalize();
                }
            }
        }
    }
    return { x: x, y: y };
}
person Jakobovski    schedule 21.10.2014
comment
Хорошая точка зрения. Вы не возражаете, если я добавлю этот чек в свой ответ? - person Tim Down; 22.10.2014
comment
@TimDown Конечно, вы можете включить это в свой ответ. - person Jakobovski; 22.10.2014

Приведенный ниже код представляет собой упрощенную и модернизированную версию решения, предоставленного Тимом Дауном. Он также использует более совместимый с браузером API выбора (window.getSelection() вместо window.document.selection)

type Coord = {
  x: number;
  y: number;
};

// atStart: if true, returns coord of the beginning of the selection,
//          if false, returns coord of the end of the selection
function getSelectionCoords(atStart: boolean): Coord | null {
  const sel = window.getSelection();

  // check if selection exists
  if (!sel.rangeCount) return null;

  // get range
  let range = sel.getRangeAt(0).cloneRange();
  if (!range.getClientRects) return null;

  // get client rect
  range.collapse(atStart);
  let rects = range.getClientRects();
  if (rects.length <= 0) return null;

  // return coord
  let rect = rects[0];
  return { x: rect.x, y: rect.y };
}
person sujenp    schedule 13.03.2021