Определить индекс позиции символа в HTML-элементе при нажатии
У меня есть элемент HTML, внутри которого есть только видимый текст. Этот пример представляет собой элемент <div>
, но он может быть <span>
, <p>
или другим элементом DOM.
<div>This is a simple example.</div>
При щелчке я могу получить позицию курсора на поверхности div, но мне нужно определить положение ближайшего символа и/или его индекса в строку div.innerHTML во время щелчка.
Я нашел аналогичную реализацию в методе getCharNumAtPosition в объектах текста SVG здесь.
Возможно ли реализовать такую функцию в JavaScript, которая работает с HTML?
(Решения были бы наиболее полезными, если бы они были переносимыми в большинстве современных браузеров, работали с большинством письменных языков и основывались на относительно стабильных стандартах, чтобы впоследствии они перестали работать.)
Ответы
Ответ 1
$('div').click( function () {
getSelectionPosition ();
});
function getSelectionPosition () {
var selection = window.getSelection();
console.log(selection.focusNode.data[selection.focusOffset]);
alert(selection.focusOffset);
}
Это работает с "click", а также с "диапазоном" для большинства браузеров. (selection.type = "caret"
/selection.type = "range"
).
selection.focusOffset()
дает вам положение во внутреннем node. Если элементы вложены, например, в теги <b>
или <span>
, это даст вам положение внутри внутреннего элемента, а не полного текста, более или менее. Я не могу "выбрать" первую букву субэлемента с типом focusOffset
и "caret" (щелчок, а не выбор диапазона). Когда вы нажимаете на первую букву, она дает позицию последнего элемента перед началом тега плюс 1. Когда вы нажимаете на вторую букву, она правильно дает вам "1". Но я не нашел способ получить доступ к первому элементу (смещение 0) вспомогательного элемента. Этот материал "выбор/диапазон" кажется багги (или очень неинтуитивным для меня). ^^
Но он довольно прост в использовании без вложенных элементов! (Отлично работает с вашим <div>
)
Вот скрипка
Важное изменение 2015-01-18:
Этот ответ сработал, когда он был принят, но уже не по причинам, приведенным ниже. Другие ответы теперь наиболее полезны.
- Общий ответ Мэтью
- Рабочий пример, представленный Дугласом Дасеке.
Оба Firefox и Chrome отлаживают поведение window.getSelection()
. К сожалению, теперь это бесполезно для этого случая использования. (Чтение документации, IE 9 и далее должно вести себя одинаково).
Теперь середина символа используется для определения смещения. Это означает, что нажатие на символ может вернуть 2 результата. 0 или 1 для первого символа, 1 или 2 для второго и т.д.
Я обновил пример JSFiddle.
Обратите внимание, что если вы измените размер окна (Ctrl + мышь), поведение довольно затруднительно для Chrome за несколько кликов.
Ответ 2
Вы можете, используя JavaScript, разбить каждый символ на свой собственный тег SPAN и добавить событие onclick для каждого. Затем прочитайте позицию x, y из данных события при щелчке по SPAN.
Кроме того, у вас будет доступ к символу, нажатому с помощью event.target.innerHTML
Ответ 3
Этот пример - быстрая загрузка блоков умеренного размера, портативных и надежных. Хотя его элегантность не сразу проявляется, надежность заключается в создании простой индивидуальной переписки между международными символами и прослушивателями событий DOM 1.
Лучше полагаться только на широко распространенные кодировки символов, DOM и ECMA, как в этом примере. Другие подходы часто полагаются на атрибуты или функции целевого события низкого уровня, такие как getSelection(), которые не являются ни стабильными с течением времени, ни переносимыми между браузерами и языками 2.
Примерный код может быть изменен несколькими способами для определенных приложений. Например, для выбора объектов DOM, инициализированных для обратных вызовов charClick, можно использовать другие механизмы. Суррогатные пары могут поддерживаться и в цикле i.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Character Click Demo</title>
<script type='text/javascript'>
var pre = "<div onclick='charClick(this, ";
var inf = ")'>";
var suf = "</div>";
function charClick(el, i) {
var p = el.parentNode.id;
var s = "para '" + p + "' idx " + i + " click";
ele = document.getElementById('result');
ele.innerHTML = s; }
function initCharClick(ids) {
var el; var from; var length; var to; var cc;
var idArray = ids.split(" ");
var idQty = idArray.length;
for (var j = 0; j < idQty; ++ j) {
el = document.getElementById(idArray[j]);
from = unescape(el.innerHTML);
length = from.length;
to = "";
for (var i = 0; i < length; ++ i) {
to = to + pre + i + inf + from.charAt(i) + suf;
el.innerHTML = to; } }
</script>
<style>
.characters div {
padding: 0;
margin: 0;
display: inline }
</style>
</head>
<body class='characters' onload='initCharClick("h1 p0 p2")'>
<h1 id='h1'>Character Click Demo</h1>
<p id='p0'>Test æ€ – ࿗Ø — string.</p>
<p id='p1'>Next 𐐷 😀E para.</p>
<p id='p2'>© 2017</p>
<hr>
<p id='result'> </p>
</body>
</html>
Прежде чем использовать это в интернационализированных сценариях производства, рекомендуется полное тестирование. Если обнаружены ошибки, предложите изменения или добавить комментарии, чтобы уточнения могли быть добавлены.
Примечания
[1] Эти прослушиватели событий могли быть добавлены напрямую, но потребовалось бы разветвление для браузера или дополнительный вес библиотек, которые не добавили бы особой скорости или удобства для этого варианта использования.
[2] См. примечания об избранных событиях в Страница событий W3C UI.
Ответ 4
Во многих случаях возможным решением может быть добавление символа пробела (U + 200B) нулевой ширины между каждым символом содержимого тега. Затем используйте javascript window.getSelection().focusOffset
+ некоторые простые вычисления выполняют эту работу.
Пример html-кода (включая пробелы в пробеле):
<div onclick='magic(this)'>This is a simple example.</div>
Пример кода javascript:
<script>
function magic(e)
{
var s, p, c;
s = e.innerHTML;
p = window.getSelection().focusOffset;
c = s.charAt((p >> 1) << 1);
alert("index:" + (p >> 1) + " char: " + c);
}
</script>
Кажется, он работает широко, даже в Интернете исследователя!
jsFidle: http://jsfiddle.net/by1a9pfm/