0

Resolution/Answer Below at bottom

Original Question

I have a LWC that end users use to complete a custom Search within Salesforce. We format the Search Bar to make reading the input clearer to the user. Unfortunately when users make a mistake in the middle of the search term and they click backspace or make an edit the cursor then jumps to the end of the search term. See the following Images:

Cursor is at the end: enter image description here

Cursor is in the Middle ready to make an edit: enter image description here

After edit is made (in this case - backspace) the cursor jumps to the End: enter image description here

LWC - HTML:

                    <!-- Account Number Search -->
                    <div onkeyup={anKeyUp} class="slds-col">
                        <lightning-layout multiple-rows="false" vertical-align="end">
                            <lightning-layout-item flexibility="auto" padding="around-small">
                                <div class="slds-form-element">
                                    <div class="slds-form-element__control">
                                        <lightning-input type="search" label="Account Number" onchange={handleCardSearchTerm}
                                            data-id="ANS" value={cardNumberFormating} max-length="19">
                                        </lightning-input>
                                    </div>
                                </div>
                            </lightning-layout-item>
                            <lightning-layout-item flexibility="auto" padding="around-small">
                                <lightning-button label="Search" variant="brand" onclick={handleCMSearch}>
                                </lightning-button>
                            </lightning-layout-item>
                        </lightning-layout>
                    </div>

LWC - JS:

    handleCardSearchTerm(event) {
        
        event.preventDefault();

        console.log('Handling Search Term Update...');
        this.cardSearchTerm = event.detail.value;
        console.log('Search Term: ', event.detail.value);

        this.reformat(event.detail.value);

    }

    // Reformating Functionality
    reformat(value){

        // Goal to reformat card search func to look like CC format xxxx-xxxx-xxxx-xxxx
        console.log(value);

        var v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, '');
        var matches = v.match(/\d{4,16}/g);
        var match = matches && matches[0] || '';
        var parts = [];
        var i;
        for (i=0; i<match.length; i+=4) {
            parts.push(match.substring(i, i+4))
        }

        if(parts.length) {
            this.cardNumberFormating = parts.join('-');
        } else {
            this.cardNumberFormating = value;
        }

        console.log('Formatted Card Number: ', this.cardNumberFormating);

    }

Ideally after a user makes an update to the search term I would like the cursor to maintain its location rather than going to the end as it currently does.

Any suggestions on how this can be completed/Resolved?

ANSWER // RESOLUTION

So Salesforce/Lightning Web Components do not provide access to the selectionStart (or SelectionEnd) Property - at least not for <lightning-input>. So in order to make the property accessible the <lightning-input> had to be created via standard HTML. See Below:

<div class="slds-form-element">
    <label class="slds-form-element__label" for="text-input-id-1">Account Number</label>
    <div
        class="slds-form-element__control slds-input-has-icon slds-input-has-icon_left">
        <svg class="slds-icon slds-input__icon slds-input__icon_left slds-icon-text-default"
            aria-hidden="true">
            <use
                xlink:href="/_slds/icons/utility-sprite/svg/symbols.svg?cache=9.37.1#search">
            </use>
        </svg>
        <input type="text" id="form-element-01"
            class="slds-input" maxlength="19" data-id="ANS"
            onkeyup={formatSearchTerm}/>
    </div>
</div>

Once this was done I was able to access the property and update it via JS as described by sfdcfox below. I did so with the following:

    formatSearchTerm(event){

        event.preventDefault();
        console.log('Formatting Input');

        this.cardSearchTerm = event.target.value;
        console.log('Search Term: ', this.cardSearchTerm);

        // Goal to reformat card search func to look like CC format xxxx-xxxx-xxxx-xxxx
        let value = event.target.value;
        let oldLength = value.length;
        value = value.replace(/\D/g, "");
        let selectionStart = event.target.selectionStart;
    
        var v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, '');
        var matches = v.match(/\d{4,16}/g);
        var match = matches && matches[0] || '';
        var parts = [];
        var i;
        for (i=0; i<match.length; i+=4) {
            parts.push(match.substring(i, i+4))
        }
        
        if(parts.length) {
            value = parts.join('-');
        } else {
            value = value;
        }

        this.cardNumberFormating = value;

        if (oldLength == selectionStart) {
          selectionStart = value.length;
        }
        event.target.value = value;
        event.target.selectionStart = selectionStart;
        event.target.selectionEnd = selectionStart;
    }   

Side Note: In my original code I used the 'value' attribute in the lightning-input and tried in the new recreated input element. This was causing issues for me accounting for the cursor jumping - this had to be removed.

Salesforce why would you move to standard HTML/JS development (LWC) but not make certain properties accessible? Doesn't make sense... Not that I don't love that you made the move, I do.

1

Before tinkering with the value, you need to keep track of selectionStart and selectionEnd, and then restore those values afterwards. What this means specifically depends on your needs, but here is a very basic demo. As you type it will add hyphens, and deleting will (mostly) navigate correctly. Note that I made this only as a quick demo. It has significant limitations, but hopefully it will help you.


import { LightningElement } from "lwc";

export default class App extends LightningElement {
  updateFormat(event) {
    let value = event.target.value;
    let oldLength = value.length;
    value = value.replace(/\D/g, "");
    let selectionStart = event.target.selectionStart;
    if (value.length > 5) {
      value =
        value.substr(0, 3) +
        "-" +
        value.substr(3, 2) +
        "-" +
        value.substr(5, 4);
    } else if (value.length > 2) {
      value = value.substr(0, 3) + "-" + value.substr(3, 2);
    }
    if (oldLength == selectionStart) {
      selectionStart = value.length;
    }
    event.target.value = value;
    event.target.selectionStart = selectionStart;
    event.target.selectionEnd = selectionStart;
  }
}

<template>
    <lightning-input onkeyup={updateFormat} label="SSN">
    </lightning-input>
</template>
1
  • When I log event.target.selectionStart in your example I see an accurate number representing the location but when I do the same thing within the LWC It coming back as undefined.
    – Max
    Oct 28 '20 at 17:42
0

ANSWER // RESOLUTION

So Salesforce/Lightning Web Components do not provide access to the selectionStart (or SelectionEnd) Property - at least not for <lightning-input>. So in order to make the property accessible the <lightning-input> had to be created via standard HTML. See Below:

<div class="slds-form-element">
    <label class="slds-form-element__label" for="text-input-id-1">Account Number</label>
    <div
        class="slds-form-element__control slds-input-has-icon slds-input-has-icon_left">
        <svg class="slds-icon slds-input__icon slds-input__icon_left slds-icon-text-default"
            aria-hidden="true">
            <use
                xlink:href="/_slds/icons/utility-sprite/svg/symbols.svg?cache=9.37.1#search">
            </use>
        </svg>
        <input type="text" id="form-element-01"
            class="slds-input" maxlength="19" data-id="ANS"
            onkeyup={formatSearchTerm}/>
    </div>
</div>

Once this was done I was able to access the property and update it via JS as described by sfdcfox. I did so with the following:

    formatSearchTerm(event){

        event.preventDefault();
        console.log('Formatting Input');

        this.cardSearchTerm = event.target.value;
        console.log('Search Term: ', this.cardSearchTerm);

        // Goal to reformat card search func to look like CC format xxxx-xxxx-xxxx-xxxx
        let value = event.target.value;
        let oldLength = value.length;
        value = value.replace(/\D/g, "");
        let selectionStart = event.target.selectionStart;
    
        var v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, '');
        var matches = v.match(/\d{4,16}/g);
        var match = matches && matches[0] || '';
        var parts = [];
        var i;
        for (i=0; i<match.length; i+=4) {
            parts.push(match.substring(i, i+4))
        }
        
        if(parts.length) {
            value = parts.join('-');
        } else {
            value = value;
        }

        this.cardNumberFormating = value;

        if (oldLength == selectionStart) {
          selectionStart = value.length;
        }
        event.target.value = value;
        event.target.selectionStart = selectionStart;
        event.target.selectionEnd = selectionStart;
    }   

Side Note: In my original code I used the 'value' attribute in the lightning-input and tried in the new recreated input element. This was causing issues for me accounting for the cursor jumping - this had to be removed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.