Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Insert character at Cursor position in VUE JS

I have been trying to insert emoji in textarea exactly where the cursor is at. I looked around how tos in the web could not find anything specific in VUE JS. Most of them are in plain JS.

I have this Code

<div class="picker" v-show="showPicker">
    <click-outside :handler="handleClickOutside">
        <picker
            set ="messenger"
            title="Pick your emoji…"
            emoji="point_up"
            @click="addEmoji"
            :emoji-size="16"
        >
        </picker> 
    </click-outside>
</div>

<textarea id="greeting_text_input" class="form-control"
    type="text" 
    v-model="greeting_text"
    rows="8"
    required
    placeholder="Hi {first-name}! Welcome to our bot. Click on the ‘Get 
    Started’ button to begin
">
</textarea>

My Method

addEmoji(emoji){
        this.greeting_text += emoji.native;
        this.showPicker = !this.showPicker;
    }

Obviously, this code will add the character (emoji, in my case) to the last of the string. I need a pure vuejs solution for this. What would be the best practise for this kind of problem in Vue? as there are few solutions in the web that based either in vanilla JS or Jquery.

like image 588
Prajwol Onta Avatar asked Jan 08 '18 07:01

Prajwol Onta


3 Answers

Two steps:

1 get textarea element using a vue-way:

1.1 Add ref attrbute to textarea tag in your template code:

<textarea ref="ta"></textarea>

1.2 get this element after mounted hook of this component:

let textarea = this.$refs.ta

2 get cursor position of textarea element.

let cursorPosition = textarea.selectionStart

Here is reference: ref

like image 94
grantonzhuang Avatar answered Nov 19 '22 03:11

grantonzhuang


<!-- tag -->
<textarea ref="yourTextarea" v-model.trim="txtContent" ......></textarea>

// methods:
insertSomething: function(insert) {
  const self = this;
  var tArea = this.$refs.yourTextarea;
  // filter:
  if (0 == insert) {
    return;
  }
  if (0 == cursorPos) {
    return;
  }

  // get cursor's position:
  var startPos = tArea.selectionStart,
    endPos = tArea.selectionEnd,
    cursorPos = startPos,
    tmpStr = tArea.value;

  // insert:
  self.txtContent = tmpStr.substring(0, startPos) + insert + tmpStr.substring(endPos, tmpStr.length);

  // move cursor:
  setTimeout(() => {
    cursorPos += insert.length;
    tArea.selectionStart = tArea.selectionEnd = cursorPos;
  }, 10);
}
like image 30
PHPJungle Avatar answered Nov 19 '22 02:11

PHPJungle


I learned about setSelectionRange from a different question, and I used it to handle credit card number input. I will show my solution here so a person can perhaps become inspired by it.

template:

<input
    ref="input"
    v-model="value"
    @input="handleChange"
>

instance methods:

data() {
    return {
        lastValue: '',
    }
},

methods: {
    setCursorPosition(el, pos) {
        el.focus();
        el.setSelectionRange(pos, pos);
    },
    handleChange() {
        // handle backspace event
        if (this.value.length < this.lastValue.length) {
            this.lastValue = this.value;
            this.$emit('input-changed', this.value);
            return;
        }
        // handle value-edit event
        if (this.$refs.input.selectionStart < this.value.length) {
            const startPos = this.$refs.input.selectionStart;
            this.value = this.value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ').trim();
            this.$nextTick(() => this.setCursorPosition(this.$refs.input, startPos));
            this.lastValue = this.value;
            this.$emit('input-changed', this.value);
            return;
        }
        // handle everything else
        this.value = this.value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ').trim();
        this.lastValue = this.value;
        this.$emit('input-changed', this.value);
    },
},

The goal with the above code is to add spaces into a credit card input, so 1234123412341234 is automatically reformatted to 1234 1234 1234 1234. A person venturing into this territory will notice that problems arise when editing the input value.

You can see there are three conditions in my sample above. The last one is the default which simply reformats the current value with a 2-step combo: remove all spaces then adds a space every 4th character.

If you comment out the two if blocks, you can watch the problems emerge.

The first if block handles the backspace event. As you can see, every time the input changes, the value is captured as this.lastValue. When you press backspace, the goal of the first condition is to NOT run the regex. In my opinion, this is better UX. If you comment out that condition, you can see.

The second if block handles the editing events. A good way to test it, is to enter a valid CC but omit the 3rd character, so that everything is off by one. Then add the character in. Everything should be good. Likewise if you backspace multiple characters out. The goal of the second condition is to properly manage the cursor position (or caret position if you prefer that nomenclature).

You can safely delete the first condition and all references to lastValue and the code will still work. This is arguably simpler but worse UX.

like image 5
agm1984 Avatar answered Nov 19 '22 03:11

agm1984