Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why selection range in textarea resets to 0?

Tags:

javascript

I have some IMG emojis which can be inserted in textarea as their string representations. A script below must get caret position and insert the representation at the right place but sometimes it misfires and reset the selection range in 0.

You can see it playing around with example clicking on smiles and inside textarea. I have reset to 0 right after setting the caret at end of textarea content.

window.onload = function() {
var doc = window.document;
var smiles = doc.getElementById('chat-smiles-panel').getElementsByTagName('IMG');
        var el    = doc.getElementById('chat-textarea');
        for (var i = 0; i < smiles.length; i++) {
            smiles[i].onclick = function(e) {
                var elem, evt = e?e:event;
                elem = (evt.target)?evt.target:evt.srcElement;
                var newText = elem.getAttribute('data-smile');
                console.log('before', el.selectionStart, el.selectionEnd);
                var start = el.selectionStart;
                var end   = el.selectionEnd;
                var text  = el.value;
                var before = text.substring(0, start);
                var after  = text.substring(end, text.length);
                el.value = (before + newText + after);
                start = end = start + newText.length;
                el.setSelectionRange(start, end);
                console.log('after', el.selectionStart, el.selectionEnd);
            }
        }
};
<div>
  <div id="chat-smiles-panel">
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/slightly-smiling-face_1f642.png" alt="smiley" data-smile=":smiley:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/rolling-on-the-floor-laughing_1f923.png" alt="lol" data-smile=":lol:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/smiling-face-with-halo_1f607.png" alt="angel" data-smile=":angel:" />
  </div>
  <textarea id="chat-textarea"></textarea>
</div>

Caret positions before and after emoji insertion seeng everytime in console log.

like image 337
Mik Avatar asked Feb 24 '19 16:02

Mik


1 Answers

As I think you know, el.value = (before + newText + after); wipes out the value and replaces it with a new value, moving the cursor back to the beginning of the field.

I think for the setSelectionRange after that to work cross-browser, the field has to have focus again. That seems to be the case in Chrome anyway, since this (with el.focus()) works:

window.onload = function() {
var doc = window.document;
var smiles = doc.getElementById('chat-smiles-panel').getElementsByTagName('IMG');
        var el    = doc.getElementById('chat-textarea');
        for (var i = 0; i < smiles.length; i++) {
            smiles[i].onclick = function(e) {
                var elem, evt = e?e:event;
                elem = (evt.target)?evt.target:evt.srcElement;
                var newText = elem.getAttribute('data-smile');
                console.log('before', el.selectionStart, el.selectionEnd);
                var start = el.selectionStart;
                var end   = el.selectionEnd;
                var text  = el.value;
                var before = text.substring(0, start);
                var after  = text.substring(end, text.length);
                el.value = (before + newText + after);
                el.focus();  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Added
                start = end = start + newText.length;
                el.setSelectionRange(start, end);
                console.log('after', el.selectionStart, el.selectionEnd);
            }
        }
};
<div>
  <div id="chat-smiles-panel">
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/slightly-smiling-face_1f642.png" alt="smiley" data-smile=":smiley:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/rolling-on-the-floor-laughing_1f923.png" alt="lol" data-smile=":lol:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/smiling-face-with-halo_1f607.png" alt="angel" data-smile=":angel:" />
  </div>
  <textarea id="chat-textarea"></textarea>
</div>

In a comment you've asked:

And no solutions if I don't want set the focus on the textarea? I asked cause I try to make UI more friendly hidding the emoji pannel when the textarea gets focus. So when the script sets focus after inserting emoji the emojis pannel slides away and it's not good.

To handle that, I tried to grab the active element before using focus:

// Didn't work
var active = document.activeElement;
el.focus();
el.setSelectionRange(start, end);
if (active) {
    active.focus();
}

When that didn't work, I punted and used el.blur(), which works (though I don't like it much):

window.onload = function() {
var doc = window.document;
var smiles = doc.getElementById('chat-smiles-panel').getElementsByTagName('IMG');
        var el    = doc.getElementById('chat-textarea');
        for (var i = 0; i < smiles.length; i++) {
            smiles[i].onclick = function(e) {
                var elem, evt = e?e:event;
                elem = (evt.target)?evt.target:evt.srcElement;
                var newText = elem.getAttribute('data-smile');
                console.log('before', el.selectionStart, el.selectionEnd);
                var start = el.selectionStart;
                var end   = el.selectionEnd;
                var text  = el.value;
                var before = text.substring(0, start);
                var after  = text.substring(end, text.length);
                el.value = (before + newText + after);
                el.focus();  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Added
                start = end = start + newText.length;
                el.setSelectionRange(start, end);
                el.blur();   // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Added
                console.log('after', el.selectionStart, el.selectionEnd);
            }
        }
};
<div>
  <div id="chat-smiles-panel">
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/slightly-smiling-face_1f642.png" alt="smiley" data-smile=":smiley:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/rolling-on-the-floor-laughing_1f923.png" alt="lol" data-smile=":lol:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/smiling-face-with-halo_1f607.png" alt="angel" data-smile=":angel:" />
  </div>
  <textarea id="chat-textarea">Testing 1 2 3</textarea>
</div>

I'd lean toward making the emojis panel a bit smarter about disappearing, such as adding a one-second delay from the last time it was used, so that if you want to add multiple emojis, you can, but then it disappears a second after the last one.

like image 199
T.J. Crowder Avatar answered Nov 15 '22 04:11

T.J. Crowder