Нажатие за пределами контентных кавычек div фокусируется на нем?
По какой-то причине мне нужно использовать contenteditable div вместо обычного ввода текста для ввода текста. (для некоторых javascript-библиотек) Он отлично работает, пока не обнаружил, что когда я устанавливаю contenteditable div с помощью display: inline-block
, он фокусируется на div, даже если я выхожу за пределы div!
Мне нужно, чтобы он фокусировался на div только тогда, когда пользователь щелкает прямо на div, но не вокруг него. В настоящее время я обнаружил, что когда пользователь щелкает в другом месте, а затем нажимает на позицию, которая является той же строкой, что и div, она фокусируется на ней.
Простой пример, чтобы показать проблему:
HTML:
<div class="outside">
<div class="text-input" contenteditable="true">
Input 1
</div>
<div class="text-input" contenteditable="true">
Input 2
</div>
<div class="unrelated">This is some unrelated content<br>
This is some more unrelated content
This is just some space to shows that clicking here doesn't mess with the contenteditable div
but clicking the side mess with it.
</div>
</div>
CSS
div.outside {
margin: 30px;
}
div.text-input {
display:inline-block;
background-color: black;
color: white;
width: 300px;
}
JSFiddle для отображения проблемы
Есть ли способ (CSS или javascript оба приемлемы), чтобы заставить браузер давать фокус только div, когда его щелкают, а не щелкая по той же строке?
P.S. Я заметил, что существует аналогичная проблема (ссылка на другой связанный пост), но ситуация немного отличается, и предоставленное решение не работает для меня.
Ответы
Ответ 1
Объяснение (если вам все равно, перейдите к разделу "Обходные пути" ниже)
Когда вы нажимаете на редактируемый элемент, браузер помещает курсор (aka точка вставки) в ближайшем текст node, который находится внутри элемента с щелчком, в той же строке, что и ваш клик. Текст node может быть либо непосредственно внутри элемента с кликом, либо в одном из его дочерних элементов. Вы можете проверить это, щелкнув по фрагменту кода ниже.
.container {width: auto; padding: 30px; background: tan;}
.container * {margin: 4px; padding: 4px;}
div {width: 50%; background: lightgreen;}
span {background: orange;}
span > span {background: gold;}
span > span > span {background: yellow;}
<div class="container" contenteditable>
text
<div>
text in a div
</div>
<span><span><span>text in spans</span></span></span></div>
Notice that you can get an insertion point by clicking above the first line or below the last. Some of the other answers don't account for this!
Ответ 2
Я смог воспроизвести это поведение только в Chrome и Safari, что говорит о том, что это может быть проблемой, связанной с Webkit.
Трудно сказать, что происходит без проверки кода, но мы можем хотя бы подозревать, что проблема заключается в некотором неисправном механизме, который запускает выбор текста в браузере.
Для аналогии, если div
не были приемлемыми, щелчок в одной строке текста после последнего символа вызовет выбор текста, начинающийся в конце строки.
Обходной путь состоит в том, чтобы обернуть contenteditable div
в элемент контейнера и создать контейнер с -webkit-user-select: none
, чтобы сделать его невыбираемым.
Как отмечает Alex Char в комментарии, это не будет препятствовать щелчку мыши за пределами контейнера, чтобы вызвать выбор в начале текста внутри него, поскольку нет статического текста между первым contenteditable div
и контейнер (предсказываемый) предка вокруг него.
Вероятнее всего, более элегантные решения, но способ преодолеть эту проблему состоит в том, чтобы вставить невидимый, непустой диапазон текста нулевой ширины непосредственно перед первым contenteditable div
, чтобы захватить нежелательный выбор текста.
- Почему не пустой?: Потому что пустые элементы игнорируются при выборе текста.
- Почему нулевая ширина?: Потому что мы не хотим ее видеть...
- Почему невидимо? Потому что мы не хотим, чтобы содержимое копировалось в буфер обмена, скажем Ctrl+A, Ctrl+C.
div.outside {
margin: 30px;
}
div.text-input {
display:inline-block;
background-color: black;
color: white;
width: 300px;
}
div.text-input-container {
-webkit-user-select: none;
}
.invisible {
visibility: hidden;
}
<div class="outside">
<div class="text-input-container">
<span class="invisible">​</span><div class="text-input" contenteditable="true">
Input 1
</div>
<div class="text-input" contenteditable="true">
Input 2
</div>
</div>
<div class="unrelated">This is some unrelated content<br>
This is some more unrelated content
This is just some space to shows that clicking here doesn't mess with the contenteditable div
but clicking the side mess with it.
</div>
</div>
Ответ 3
Если вам не нужно использовать display: inline-block
, я бы рекомендовал использовать float. Вот пример.
На основе вашего примера новый CSS будет выглядеть следующим образом:
div.text-input {
display: block;
background-color: black;
color: white;
width: 300px;
float: left;
margin-right: 10px;
}
div.unrelated {
clear: both;
}
Ответ 4
Отключить выбор текста в контейнере... должен исправить это.
Например:
* {
-ms-user-select: none; /* IE 10+ */
-moz-user-select: -moz-none;
-khtml-user-select: none;
-webkit-user-select: none;
user-select: none;
}
Ответ 5
Как насчет небольшого jQuery?
$(".outside").click(function(e){
$(e.target).siblings(".text-input").blur();
window.getSelection().removeAllRanges();
});
И если IRL вам нужно будет зачислить клики на дочерях contenteditable=true
siblings:
$(".outside").click(function(e){
if ($(e.target).siblings(".text-input").length != 0){
$(e.target).siblings(".text-input").blur();
window.getSelection().removeAllRanges();
}
else {
$(e.target).parentsUntil(".outside").last().siblings(".text-input").blur();
window.getSelection().removeAllRanges();
}
});
window.getSelection().removeAllRanges();
"Уловка заключается в том, чтобы удалить все диапазоны после размытия вызова"