用户选择:在Safari中没有人表现出不同

时间:2019-08-19 09:23:52

标签: javascript html css vue.js rangy

我要实现的目标

我正在构建类似输入内容的可编辑div。您应该单击div外部的一些标签以将其添加到div中,同时也可以在所述标签周围输入内容。

问题及其重现方法

我正在使用用户选择:none(正常和webkit)使标签按钮不被选中,因此失去了插入符号的位置。它可以在Firefox和Chrome中使用,但不能在Safari中使用(我知道-webkit-前缀并正在使用它)。

Here is a fiddle where you can reproduce the problem.

我尝试过的

我的问题的根源是保持插入符号的位置,同时使内容的div可编辑。

我以前曾尝试使用rangy,但在有关Firefox的某些限制中陷入困境。从UX的角度来看,这些限制非常令人讨厌。您可以针对此用户选择的内容检查我以前的问题及其解决方法:无解-Caret disappears in Firefox when saving its position with Rangy

这就是我采用用户选择的解决方案的方式:无。

我的组件/ 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粘贴的。我建议使用小提琴来检查代码,重现问题。

预期行为与实际结果

在Safari(最新版本)中,如果我键入一个单词,然后单击该单词的某个位置,或者通过键盘箭头将该插入符号移动到该单词中,然后单击输入右侧的标签之一,则该标签应会被添加到被点击单词的中间(在此处进行选择),但会被添加到单词的开头。

tl; dr:单击其中一个标签时,Safari不尊重插入符号的位置。它将标签添加到内容可编辑div的开头,而不是插入符号之前的位置。

编辑1 :基于这些日志,getSelection()告诉我们偏移量始终为0,因为在Safari中,div失去了焦点。 enter image description here

1 个答案:

答案 0 :(得分:1)

看来您基本上已经找到了答案。这是一个时间问题。

如果您将事件更改为mousedown,则插入符号的位置不会丢失,并且标签会插入正确的位置。

<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事件中仍具有正确的位置。