问题描述
我正在构建类似输入内容的可编辑div.您应该单击div外部的一些标签以将其添加到div中,同时也可以在所述标签周围输入内容.
I am building input-like content editable div. You are supposed to click some tags outside the div to add them inside the div while also being able to type around said tags.
我正在使用用户选择:none(正常和webkit)使标签按钮不被选中,因此失去了插入符号的位置.它可以在Firefox和Chrome中运行,但不能在Safari中运行(我知道-webkit-前缀并正在使用它).
I am using user-select: none (normal and webkit) to keep tag buttons from being selected, therefore losing my caret's position. It works in Firefox and Chrome but not in Safari (I aware of the -webkit- prefix and using it).
我的问题的根源是保持插入符号的位置,同时使内容的div保持可编辑状态.
The root of my problem was maintaining the caret's position while leaving the content editable div.
我以前曾尝试使用rangy,但在有关Firefox的某些限制中陷入困境.从UX的角度来看,这些限制非常令人讨厌.您可以针对此用户选择检查我以前的问题及其解决方法:无解决方案-
I have previously tried to use rangy but got stuck in some limitations regarding Firefox. These limitations where quite annoying from an UX standpoint. You can check my previous question and how it got me here, to this user-select: none solution -Caret disappears in Firefox when saving its position with Rangy
这就是我采用用户选择的解决方案的方式:无.
That's how I got to this solution featuring user-select: none.
我的组件/JS:
new Vue({
el: "#app",
data(){
return {
filters_toggled: false,
fake_input_content: '',
input_length: 0,
typed: false,
boolean_buttons: [{
type: '1',
label: 'ȘI',
tag: 'ȘI',
img: 'https://i.imgur.com/feHin0S.png'
}, {
type: '2',
label: 'SAU',
tag: 'SAU',
img: 'https://i.imgur.com/vWJeJwb.png'
}, {
type: '3',
label: 'NU',
tag: 'NU',
img: 'https://i.imgur.com/NNg1spZ.png'
}],
saved_sel: 0,
value: null,
options: ['list', 'of', 'options']
}
},
name: 'boolean-input',
methods: {
inputLength($event){
this.input_length = $event.target.innerText.length;
if(this.input_length == 0)
this.typed = false;
},
addPlaceholder(){
if(this.input_length == 0 && this.typed == false){
this.$refs.divInput.innerHTML = 'Cuvinte cheie, cautare booleana..'
}
},
clearPlaceholder(){
if(this.input_length == 0 && this.typed == false){
this.$refs.divInput.innerHTML = '';
}
},
updateBooleanInput($event){
this.typed = true;
this.inputLength($event);
},
saveCursorLocation($event){
/*
if($event.which != 8){
if(this.saved_sel)
rangy.removeMarkers(this.saved_sel)
this.saved_sel = rangy.saveSelection();
}
*/
// if(this.input_length == 0 && this.typed == false){
// var div = this.$refs.divInput;
// var sel = rangy.getSelection();
// sel.collapse(div, 0);
// }
},
insertNode: function(node){
var selection = rangy.getSelection();
var range = selection.getRangeAt(0);
range.insertNode(node);
range.setStartAfter(node);
range.setEndAfter(node);
selection.removeAllRanges();
selection.addRange(range);
},
addBooleanTag($event){
// return this.$refs.ChatInput.insertEmoji($event.img);
if (!this.$refs.divInput.contains(document.activeElement)) {
this.$refs.divInput.focus();
}
console.log(this.input_length);
if(this.typed == false & this.input_length == 0){
this.$refs.divInput.innerHTML = ''
var space = '';
this.typed = true
//this.saveCursorLocation($event);
}
//rangy.restoreSelection(this.saved_sel);
console.log(getSelection().anchorNode, getSelection().anchorOffset, getSelection().focusNode, getSelection().focusOffset)
var node = document.createElement('img');
node.src = $event.img;
node.className = "boolean-button--img boolean-button--no-margin";
node.addEventListener('click', (event) => {
// event.currentTarget.node.setAttribute('contenteditable','false');
this.$refs.divInput.removeChild(node);
})
this.insertNode(node);
this.saveCursorLocation($event);
},
clearHtmlElem($event){
var i = 0;
var temp = $event.target.querySelectorAll("span, br");
if(temp.length > 0){
for(i = 0; i < temp.length; i++){
if(!temp[i].classList.contains('rangySelectionBoundary')){
if (temp[i].tagName == "br"){
temp[i].parentNode.removeChild(temp[i]);
} else {
temp[i].outerHTML = temp[i].innerHTML;
}
}
}
}
},
pasted($event){
$event.preventDefault();
var text = $event.clipboardData.getData('text/plain');
this.insert(document.createTextNode(text));
this.inputLength($event);
this.typed == true;
},
insert(node){
this.$refs.divInput.focus();
this.insertNode(node);
this.saveCursorLocation($event);
},
fixDelete(){
}
},
props: [ 'first'],
mounted() {
this.addPlaceholder()
}
})
我的HTML
<div id="app">
<div class="input__label-wrap">
<span class="input__label">Cauta</span>
<div style="user-select: none; -webkit-user-select: none">
<span readonly v-on:click="addBooleanTag(b_button)" v-for="b_button in boolean_buttons" class="boolean-buttons">{{b_button.label}}</span>
</div>
</div>
<div class="input__boolean input__boolean--no-focus">
<div
@keydown.enter.prevent
@blur="addPlaceholder"
@keyup="saveCursorLocation($event); fixDelete(); clearHtmlElem($event);"
@input="updateBooleanInput($event); clearHtmlElem($event);"
@paste="pasted"
v-on:click="clearPlaceholder(); saveCursorLocation($event);"
class="input__boolean-content"
ref="divInput"
contenteditable="true">Cuvinte cheie, cautare booleana..</div>
</div>
</div>
我的CSS
.filters__toggler
{
cursor: pointer;
padding: 2px;
transition: all 0.2s ease-in-out;
margin-left: 10px;
}
.filters__toggler path
{
fill: #314964;
}
.filters__toggler-collapsed
{
transform: rotate(-180deg);
}
.input__label
{
font-family: $roboto;
font-size: 14px;
color: #314964;
letter-spacing: -0.13px;
text-align: justify;
}
.input__boolean
{
width: 100%;
background: #FFFFFF;
border: 1px solid #AFB0C3;
border-radius: 5px;
padding: 7px 15px 7px;
font-family: $opensans;
font-size: 14px;
color: #082341;
min-height: 40px;
box-sizing: border-box;
margin-top: 15px;
display: flex;
flex-direction: row;
align-items: center;
line-height: 22px;
overflow: hidden;
}
.input__boolean-content
{
width: 100%;
height: 100%;
outline: none;
border: none;
position: relative;
padding: 0px;
word-break: break-word;
}
.input__boolean img
{
cursor: pointer;
margin-bottom: -6px;
}
.input__boolean--no-focus
{
color: #9A9AA6
}
.input__label-wrap
{
display: flex;
justify-content: space-between;
width: 100%;
position: relative;
}
.boolean-buttons
{
background-color: #007AFF;
padding: 3px 15px;
border-radius: 50px;
color: #fff;
font-family: $roboto;
font-size: 14px;
font-weight: 300;
cursor: pointer;
margin-left: 10px;
}
.boolean-button--img
{
height: 22px;
margin-left: 10px;
}
.boolean-button--no-margin
{
margin: 0;
}
.popper
{
background-color: $darkbg;
font-family: $opensans;
font-size: 12px;
line-height: 14px;
color: #fff;
padding: 4px 12px;
border-color: $darkbg;
box-shadow: 0 5px 12px 0 rgba(49,73,100,0.14);
border-radius: 8px;
}
.filters__helper
{
cursor: pointer;
margin-left: 10px;
margin-bottom: -3px;
}
.popper[x-placement^="top"] .popper__arrow
{
border-color: #082341 transparent transparent transparent;
}
注意:忽略新的Vue,它是从 Fiddle 粘贴的.我建议使用小提琴来检查代码,重现问题.
Note: ignore the new vue, it's pasted from the Fiddle. I would suggest using the fiddle to inspect the code, reproduce the problem.
在Safari(最新版本)中,如果我键入一个单词,然后单击该单词的某个位置,或者通过键盘箭头将该插入符号移动到该单词中,然后单击输入右侧的标签之一,则该标签应会被添加到被单击单词的中间(在此处进行选择),但会添加到单词的开头.
In Safari (latest version), if I type a word and then click somewhere in that word or move the caret in that word through the keyboard arrows then click one of the tags on the right side of the input, the tag should be added in the middle of clicked word (where was the selection made) but it is added at the beginning of the word.
tl; dr:单击其中一个标签时,Safari不尊重插入符号的位置.它将标签添加到内容可编辑div的开头,而不是插入标记之前的位置.
tl;dr: Safari does not respect the caret's position when clicking one of the tags. It adds the tag at the beginning of the content editable div, not where the caret previously was.
编辑1 :基于这些日志,getSelection()告诉我们偏移量始终为0,因为在Safari中,div失去了焦点.
Edit 1: Based on these logs, getSelection() teaches us that the offset is always 0 because in Safari, the div loses focus.
推荐答案
似乎您已经基本上找到了答案.这是一个时间问题.
It seems you basically found the answer yourself already. It is a timing issue.
如果您将事件更改为mousedown,则插入符号的位置不会丢失,并且标签会插入正确的位置.
If you change the event to mousedown, the caret position isn't lost and the tag gets inserted at the correct position.
<div id="app">
<div class="input__label-wrap">
<span class="input__label">Cauta</span>
<div style="user-select: none; -webkit-user-select: none">
<span readonly v-on:mousedown="addBooleanTag(b_button)" v-for="b_button in boolean_buttons" class="boolean-buttons">{{b_button.label}}</span>
</div>
</div>
<div class="input__boolean input__boolean--no-focus">
<div
@keydown.enter.prevent
@blur="addPlaceholder"
@keyup="saveCursorLocation($event); fixDelete(); clearHtmlElem($event);"
@input="updateBooleanInput($event); clearHtmlElem($event);"
@paste="pasted"
v-on:click="clearPlaceholder(); saveCursorLocation($event);"
class="input__boolean-content"
ref="divInput"
contenteditable="true">Cuvinte cheie, cautare booleana..</div>
</div>
</div>
https://jsfiddle.net/xmuzp20o/
如果您不想在mousedown上添加实际标签,则可以至少在该事件中保存脱字号的位置,以便在click事件中仍具有正确的位置.
If you don't want to add the actual tag on mousedown, then you could save the caret position at least in that event, so that you still have the correct position in the click event.
这篇关于用户选择:在Safari中没有人表现出不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!