vue-input-contenteditable icon indicating copy to clipboard operation
vue-input-contenteditable copied to clipboard

设置输入最长20字后,ios输入超过20字时,光标错乱

Open ohion opened this issue 4 years ago • 3 comments

ios输入超过20字时,虽然输入不了,但是光标一直在走,在往前走,请问这个怎么解决

ohion avatar Feb 01 '21 08:02 ohion

When ios input more than 20 characters, although the input cannot be made, the cursor keeps moving, and it is moving forward. How can I solve this?

The cursor keeps moving forward, like, literally moving in the x direction but no characters are input?

I will hopefully have an iOS device in 1 week and maybe I can test then. Can you maybe send a video of the issue?

Cobertos avatar Feb 13 '21 03:02 Cobertos

你好,我改了一下代码,主要就是加了一个变量isChange,以下是我的代码:

<!-- Created by dreamsqin on 2019/9/5 -->
<template>
  <div
    class="div-editable"
    contenteditable="true"
    v-html="innerText"
    ref='contenteditable'
    @input="changeText"
    @focus="isChange = false"
    @change="changeFunc"
    @blur="blurFunc"></div>
</template>

<script>
  export default {
    name: 'DivEditable',
    props: {
      value: {
        type: String,
        default: ''
      },
      maxlength: {
        type: Number,
        default: -1
      }
    },
    data() {
      return {
        innerText: this.value,
        isChange: true,
        lastText: this.value //Initally set to value if exists
      }
    },
    watch: {
      value() {
        if (this.isChange) {
          this.innerText = this.value
        }
      }
    },
    methods: {
      async changeText (e) {
        let text = this.$refs.contenteditable.textContent;
        if (this.maxlength !== -1) {

          //I chose this instead of preventDefault on 'keydown', 'paste', 'drop' as if we preventDefault
          //we need to check a bunch of specific valid cases to pass through like backspace, delete
          //Ctrl+A, A Ctrl+V that makes the text shorter, arrow keys, etc. which may be impossible...
          //
          //Instead, retroactively trimming the string after 'input' and setting the cursor properly
          //(as changing the text string will change the cursor in some browsers... :( ) is a better bet
          //IMO. Current method was tested in Chrome, FF, and Android

          let selection = window.getSelection();
          let { anchorNode, anchorOffset } = selection;
          if (text.length > this.maxlength) {
            //Find the cursor position inside the contenteditable. Can't use anchorOffset
            //because Firefox will add multiple text nodes when pasting sometimes
            //(and then collapse them later? it's kind of weird...)
            const textNodes = Array.from(this.$refs.contenteditable.childNodes);
            const realAnchorOffset = textNodes.length <= 1 ? anchorOffset : (
              textNodes
                //Collect all nodes up to, but not including, anchorNode
                .slice(0, textNodes.indexOf(anchorNode))
                //Map them all to their length
                .map(n => n.textContent.length)
                //Sum them together
                .reduce((acc, itm) => acc + itm, 0) +
                //And then add the final offset in the final node
                anchorOffset);

            //Use either the lastText if exists, or the current text but trimmed
            const newTextToSet = this.lastText || text.slice(0,this.maxlength);

            //Find the last position of the cursor before the input event. Use the
            //current cursor position, and remove the difference between the untrimmed text
            //and the trimmed text (to back the cursor up to the position the
            //input event happened at)
            //We can't use anchorOffset because FF likes to make new text nodes
            //for pasted text for some reason??
            let newOffsetToSet = realAnchorOffset - (text.length - newTextToSet.length);
            newOffsetToSet = Math.min(newOffsetToSet, this.maxlength); // Make sure not over maxlength
            //console.log(realAnchorOffset, anchorOffset, text.length, newTextToSet.length, this.$refs.contenteditable.childNodes.length);

            //This will reset the cursor to the start of the contenteditable _and_
            //make a new text node (so don't use anchorNode for selection.collapse())
            this.$refs.contenteditable.textContent = newTextToSet;

            //Set selection using last valid offset
            selection.collapse(this.$refs.contenteditable.childNodes[0], newOffsetToSet);
            this.lastText = newTextToSet;
            return;
          } else {
            this.lastText = text;
          }
        }
        // this.$emit('input', this.$el.innerHTML)
        this.$emit('input', text);
      },
      blurFunc() {
        this.isChange = true
        this.$emit('blurFunc')
      },
      changeFunc(e){
        console.log(e.target.value)
        if(e.target.value.length>20){
          alert("error")
        }

      }
    }
  }
</script>

<style lang="scss">
  .div-editable{
    width: 100%;
    height: 100%;
    overflow-y: auto;
    word-break: break-all;
    outline: none;
    user-select: text;
    white-space: pre-wrap;
    text-align: left;
    &[contenteditable=true]{
      user-modify: read-write-plaintext-only;
      &:empty:before {
        content: attr(placeholder);
        display: block;
        color: #ccc;
      }
    }
  }
</style>

ohion avatar Mar 20 '21 10:03 ohion

Hmm, okay, thank you for the solution.

This should be replaced with something more robust when I next loop back to this project.

Cobertos avatar Apr 20 '21 07:04 Cobertos