JavaScript: Move the cursor to a specified position while updating the input value

In my application, I am aiming to create a user experience similar to Microsoft Excel / Google Sheets where there is an autocomplete dropdown for formulas and variables. Once the autocomplete has been validated, I want to have control over the cursor position.

For example, if I enter =sum(variable1, variable2), when selecting variable2 from the autocomplete, I want the cursor to appear before the last parenthesis rather than at the very end.

I know how to adjust the cursor position using JavaScript, but the issue arises when I try to modify the input value and set the cursor position simultaneously - it doesn't seem to work properly.

I've replicated the problem on a simpler scale on fiddle: https://jsfiddle.net/joparisot/j8ourfa1/31/

Here is the HTML code snippet:

<div id="app">
    <autocomplete v-model="selection"></autocomplete>
</div>

<template id="autocomplete">
  <div>
    <h2>gernerogrnio</h2>
    <input id="my-input" 
           class="form-control" 
           type="text" 
           :value="value"
           @keydown.enter="updateValue($event.target.value)">
    <p>{{ value }}</p>
  </div>
</template>

And here is the script section:

Vue.component('autocomplete', {
    template: '#autocomplete', 
  props: {
    value: {
      type: String,
      required: true
    }
  }, 
  methods: {
    updateValue (value) {
        var new_value = ''
      if (value.length < 4) {
        new_value = 'Inferior'
      } else {
        new_value = 'Superior'
      }

      this.$emit('input', new_value)
      var myInput = document.getElementById('my-input');
      this.setCaretPosition(myInput, 5)
    }, 
    setCaretPosition(ctrl, pos) {
        ctrl.focus();
        ctrl.setSelectionRange(pos, pos);
    }
  }
});

new Vue({
    el: '#app', 
  data: {
    selection: 'test'
  }
});

The focus here is not on the autocomplete functionality, but rather on adjusting the input value after pressing enter. You'll notice that setting the cursor position will work if you comment out lines 11 to 16 and simply assign the new_value to the existing value.

I'm struggling to achieve both tasks simultaneously. Any insights?

Answer №1

Big shoutout to Roy J for his helpful comment that led me straight to the solution.

To properly update the value, make sure to include the code below within the updateValue function:

this.$nextTick(() => {
  this.setCaretPosition(myInput, 5)
});

Answer №2

I came across the setSelectionRange function in this discussion, and it proved to be extremely useful when dealing with credit card number input:

Here's a snippet of the code I implemented:

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

In my implementation, I included the following instance methods:

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

methods: {
    setCursorPosition(el, pos) {
        el.focus();
        el.setSelectionRange(pos, pos);
    },
    handleChange() {
        // Handling backspace event
        if (this.value.length < this.lastValue.length) {
            this.lastValue = this.value;
            this.$emit('input-changed', this.value);
            return;
        }
        // Handling 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;
        }
        // Default handling for other cases
        this.value = this.value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ').trim();
        this.lastValue = this.value;
        this.$emit('input-changed', this.value);
    },
},

The main objective of the above code snippet is to automatically insert spaces into a credit card input, transforming something like 1234123412341234 to 1234 1234 1234 1234. However, editing the input value can lead to some challenges.

As demonstrated in my sample above, there are three conditions outlined. The final condition serves as the default, applying a regex pattern that inserts a space after every 4 characters while removing all existing spaces.

If you disable the two if blocks, you will encounter issues during input editing.

The initial if block focuses on handling backspace events effectively. By capturing the current value as this.lastValue, the intention is to prevent running the regex operation when the backspace key is pressed - enhancing user experience. Deleting this condition would showcase these challenges.

The second if block is dedicated to managing editing events. To test its functionality, deliberately omit the 3rd character in a valid credit card number and then correct the omission. Proper caret management ensures smooth transitions with each edit, even for multiple backspaces performed. This condition aims to maintain cursor position accuracy.

Even without the first condition and references to lastValue, the code will still operate smoothly. While this could streamline the implementation, it may compromise user experience.

Answer №3

After extensive testing in both IE and Chrome, I have discovered a simple solution to resolve this issue.

Simply invoke the following function with each keystroke:

function moveCaret(element) {
   var cursorPosition = $(element)[0].selectionStart;
   var selectedElement = document.getElementById(element);
   selectedElement.setSelectionRange(cursorPosition + 1, cursorPosition + 1);
}

Provide the text box ID as an argument to this function, and it will automatically determine the mouse position and adjust the caret accordingly for each key press.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

React-Native 0.1.17 Navigator Bar: Enhancing User Navigation Experience

An issue arose after upgrading my react-native 0.1.15 app to version 0.1.17 - I'm now encountering an 'Unable to download JS bundle error'. Upon investigation, I found the error in my code: var SportsSocial = React.createClass({ component ...

Connecting Websockets in AngularJs for Message Binding

I've hit a roadblock with my mini project, and I have a hunch it's something simple... My challenge is binding websocket messages to an Angular datamodel, but I can't seem to make it work... Here is my controller and some HTML to display t ...

Creating a javascript function to update content on click

Recently, I've been designing a webpage and encountered an issue. I want the text in a specific area to change whenever a user clicks on a link. Below is the code snippet related to the section I want to modify using a JavaScript function. <div id ...

Issues arise when props do not get transferred successfully from the getStaticPaths() to the getStaticProps

I have successfully generated dynamic pages in nextJS from a JSON using getStaticPaths(). However, I am facing an issue where I am unable to access the information within the JSON. I pass it as props to getStaticProps(), but when I try to console log it, i ...

Prevent the Rain from descending

Looking for a way to toggle a particle emitter on or off, I've encountered memory leaks with my Reactjs code that generates rain/snow particles using the canvas element. Despite attempts to stop the animation properly, it seems to be projecting a new ...

Using style binding and ngStyle doesn't appear to be effective for setting a background image within a DIV element in Angular5

After facing difficulties in customizing the spacing of Angular material cards, I decided to create my own cards. However, during this process, I encountered an issue where I needed to set an image as a background inside a div. Despite trying various CSS ...

Tips for personalizing the tooltip on a line chart in Vue.js using Chart.js

Attempting to create a line graph using chart js and vue, but new to both technologies. In the chart below, I am trying to change the tooltip to display "10 Answers July 19, 2023". I have attempted to use the beforeLabel and afterLabel options without succ ...

What could be causing the network error that keeps occurring when attempting to sign in with Google using React and Firebase?

Having an issue with Google authentication using Firebase. I set up the project on Firebase console and copied the configuration over to my project. Enabled Google sign-in method in the console which was working fine initially. But, after 2 days when I tri ...

Exploring the dynamic duo of HTML and JQuery within the Webstorm IDE

I'm facing a simple issue with my webstorm project. It consists of an HTML file, CSS file, and JS file. In my project, I tried creating a circle using a "div" element and attempted to make it fade out upon clicking with JavaScript. However, the funct ...

Eliminate set of dates utilizing a different set of dates

Utilizing the MultipleDatePicker plugin to enable selection of multiple dates within a year. I have incorporated a checkbox feature that, when checked, will automatically mark all Sundays in the calendar. However, an issue arises when unchecking the check ...

Manually update the DOM rather than depending on React's reconciliation process

Creating a React component that wraps a complex rendering API is my current goal. This specific API has the ability to render a plot into HTML+SVG and also offers functionality to update the plot with new data efficiently. I am keen on utilizing this updat ...

Substituting text in a document by utilizing two separate arrays: one holding the original text to be found and another storing the corresponding text for

I am facing a challenge with replacing specific text strings in a file. I have two arrays - one containing the strings that need to be located and replaced, and the other containing the replacement strings. fs.readFile("./fileName.L5X", "utf8", function( ...

Differences in Print Layout Between Chrome and Firefox when Viewing a PHP Website

I have been working on creating a print command to print a specific div, and I have managed to successfully run the print command using default methods like CTRL + P and also with a button click. The Issue: However, I have encountered a problem where the ...

Issue with joining tables in query on Cordova Mobile app

I have 2 queries that will return results which I plan to use in JSON format. The first query is $query = "SELECT * FROM info_location WHERE location_id=".$id.""; and the second query $query = "SELECT t1.location_id,t1.street,t1 ...

Reorganizing Arrays in Javascript: A How-To Guide

I have an array in JavaScript called var rows. [ { email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="81f4f2e4f3b0c1e4f9e0ecf1ede4afe2eeec">[email protected]</a>' }, { email: '<a hre ...

What steps should I take to resolve the problem with accessing Mongodb?

Whenever I attempt to initialize MongoDb by typing 'mongo', I keep getting the error message "not recognized as an internal or external command". Despite setting the environment variables as recommended by many, as shown in this screenshot, I am ...

How can I adjust the size of the renderer in Three.js to fit the screen?

Encountering issues with the code snippet below: //WebGL renderer renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); //TODO: set optimal size document.body.appendChild(renderer ...

In what way can data be retrieved from within a useEffect block externally?

Essentially, I'm facing an issue with retrieving data from the DateView component in my ChooseCalendar component. The code that retrieves this data is located within a useEffect block due to passing a dateType variable as part of a useState to the com ...

What is the process for creating a new Object based on an interface in Typescript?

I am dealing with an interface that looks like this: interface Response { items: { productId: string; productName: string; price: number; }[] } interface APIResponse { items: { productId: string; produc ...

Issues with AngularJS have arisen, as it is currently unable to recognize and access variables as needed

As a newcomer to web design and Angular, I am trying to replicate this example, where I consume a RESTful web service and use AngularJS to display the JSON data. However, I'm facing issues with my implementation. Here's how my main.jsp file look ...