#html #contenteditable
#HTML #contenteditable
Вопрос:
В редактируемом содержимом div я хочу окружить выделенный текст символом <span>
или изменить существующий <span>
, когда я выделяю весь текст внутри него. Я могу сделать первое, но не второе.
Во втором случае, если у меня есть A<span>B</span>C
, и я выбираю B , в выделении отображается anchorNode
as A
, focusNode
as C
и toString()
возвращает B
. Родительский узел — это заключающий абзац, а не <span>
. Я не могу найти никакого способа отличить выбор B
in A<span>B</span>C
и in ABC
, поскольку я не могу найти никакого способа обнаружить существование <span>
элемента, который окружает B
в первом случае. Должен же быть способ сделать это, не так ли? Может кто-нибудь сказать мне, как это сделать?
Вот код:
var sel = window.getSelection();
var range = sel.getRangeAt(0);
var selected = range.compareBoundaryPoints(range.START_TO_END, range);
if (selected > 0) { // some text is selected
var el = range.commonAncestorContainer;
//
// would expect to find el == <span> if selection is B
// in <p>A<span>B</span>C<p>, but find <p> instead!
//
//... code to surround selection with new span or to process
// existing span... but there is never an existing span!
}
Комментарии:
1. В какой среде вы пытаетесь это сделать? У вас есть еще код?
2. Добавлен некоторый код выше. Что я делаю: использую Firefox, дважды щелкаю по слову в абзаце внутри редактируемого содержимого div, добавляю интервал (который работает), затем дважды щелкаю по тому же слову, пытаюсь изменить интервал, который я только что вставил, и устанавливаю точку останова в отладчике браузера сразу послеприсвоение «el». Значение «el» является диапазоном, ТОЛЬКО если я нажимаю кнопку, чтобы изменить диапазон сразу после его вставки — если я щелкну где-нибудь еще, затем вернусь и дважды щелкните слово еще раз, оно не сможет найти диапазон, даже если выбранный текст полностью находится внутри него.
3. Поскольку он не находит диапазон, я получаю <p>A <span><span>B</span></span> C</p> .
Ответ №1:
Range.commonAncestorContainer имеет свойство parentElement . Это родительский элемент выделенного текста.
Во фрагменте кода и Лондон, и Париж обернуты элементом span .
const text = "<p>Hello from <span>London</span> and <span>Paris</span></p>"
const parser = new DOMParser();
const html = parser.parseFromString(text, "text/html").body.innerHTML
const editor = document.querySelector('div[contenteditable]')
editor.innerHTML = html
function getParent() {
var selection = window.getSelection();
if (selection.rangeCount) {
const range = selection.getRangeAt(0);
const parent = range.commonAncestorContainer.parentElement;
document.querySelector("#result").innerText = parent instanceof HTMLSpanElement
if (parent instanceof HTMLSpanElement) {
console.log(parent)
}
}
}
document.querySelector("button").onclick = getParent;
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css" rel="stylesheet" />
<div class="container py-5">
<div class="row align-items-center">
<div class="col">
<div class="w-100 p-5 border" contenteditable>
</div>
</div>
<div class="col">
<button class="btn btn-primary">Get Text</button>
</div>
</div>
<div class="row align-items-center">
<div class="col">
<p>Is Is parent instanceof HTMLSpanElement? <span id="result"></span> </p>
</div>
</div>
</div>
Комментарии:
1. Это работает только до тех пор, пока вы выбираете текст в пределах диапазона, а не все содержимое диапазона. Например. если вы выбираете все слово «Лондон», общим предком является <span><p>, который заключает в , а parentElement — это тогда заключающий <div> из> . Я пытаюсь разобраться со случаем, когда выделение представляет собой содержимое всего диапа зона. Кажется, что когда вы выбираете «Лондон» в приведенном выше, вы на самом деле выбираете «<span>Лондон</span>», и, похоже, нет простого способа отличить «Лондон» от «<span>Лондон</span>»
2. Рассмотрите возможность изменения цвета шрифта путем переноса текста <span>. Если текст не является содержимым всего диапазона (например, «ondo» внутри «London»), тогда я оборачиваю его, чтобы дать, например, <span> L <span> ondo</spa n> n</span> . Если я выберу весь <span> <span> диапазон, я получу Лондон, который все еще синий</span></span>, потому что я оборачиваю за пределы существующ его диапазона, а не внутри. В этом случае я хочу просто изменить охватывающий диапазон с синего на красный.
3. @user1636349 London — это экземпляр текста, а <span>London</span> — экземпляр HTMLS panElement . Если вы добавите все сценарии с примерами [действие, ожидаемый результат], я вам помогу. Если вы это сделаете, включите его в свой вопрос.
4. Ну, в вашем примере, если вы выберете слово «Лондон» и нажмете «Получить текст», ничего не произойдет (потому что выбран весь диапазон, а его родительским элементом является <p> ), но если вы выберете «ondo» из середины слова, там будет написано «<span>Лон дон</span>». Мне бы хотелось, чтобы при выборе «ondo» в середине слова было написано «ondo», а при выборе «Лондон» отображалось «<span>Лондон</span>».
5. Как я уже сказал ниже, у меня есть обходной путь, но это полная ошибка. Так что более аккуратное решение было бы высоко оценено!
Ответ №2:
Я придумал довольно ужасный обходной путь для вышеупомянутой проблемы. Чтобы проверить, является ли выделение целым <span>
элементом, я клонирую диапазон, который, когда это весь диапазон, содержит диапазон, которому предшествуют и за которым следуют пустые строки. Итак, что я делаю, это удаляю любой из них и проверяю, остался ли у меня один элемент span. Если это так, я беру следующий элемент после начала исходного диапазона, который будет <span>
:
var el = range.commonAncestorContainer;
var copy = range.cloneContents();
while (copy.childNodes.length > 1 amp;amp; copy.firstChild.nodeValue === "") {
copy.removeChild(copy.firstChild);
}
while (copy.childNodes.length > 1 amp;amp; copy.lastChild.nodeValue === "") {
copy.removeChild(copy.lastChild);
}
if (copy.childNodes.length == 1 amp;amp; copy.childNodes[0] instanceof HTMLSpanElement) {
el = range.startContainer.nextSibling;
}
Это работает, но не очень привлекательно. Я был бы рад, если у кого-нибудь есть более аккуратное решение.