Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript: Scroll to selection after using textarea.setSelectionRange in Chrome

A JavaScript function selects a certain word in a textarea using .setSelectionRange().

In Firefox, the textarea automatically scrolls down to show the selected text. In Chrome (v14), it does not. Is there a way to get Chrome to scroll the textarea down to the newly selected text?

jQuery solutions are welcome.

like image 408
hamboy Avatar asked Sep 18 '11 20:09

hamboy


5 Answers

Here is a simple and efficient solution in pure JavaScript:

// Get the textarea
var textArea = document.getElementById('myTextArea');

// Define your selection
var selectionStart = 50;
var selectionEnd = 60;
textArea.setSelectionRange(selectionStart, selectionEnd);

// Mow let’s do some math.
// We need the number of characters in a row
var charsPerRow = textArea.cols;

// We need to know at which row our selection starts
var selectionRow = (selectionStart - (selectionStart % charsPerRow)) / charsPerRow;

// We need to scroll to this row but scrolls are in pixels,
// so we need to know a row's height, in pixels
var lineHeight = textArea.clientHeight / textArea.rows;

// Scroll!!
textArea.scrollTop = lineHeight * selectionRow;

Put this in a function, extend the prototype of JavaScript's Element object with it, and you're good.

like image 103
Frederik Eychenié Avatar answered Oct 30 '22 22:10

Frederik Eychenié


A lot of answers, but the accepted one doesn't consider line breaks, Matthew Flaschen didn't add the solution code, and naXa answer has a mistake. The simplest solution code is:

textArea.focus();

const fullText = textArea.value;
textArea.value = fullText.substring(0, selectionEnd);
textArea.scrollTop = textArea.scrollHeight;
textArea.value = fullText;

textArea.setSelectionRange(selectionStart, selectionEnd);
like image 20
Valeriy Katkov Avatar answered Oct 30 '22 22:10

Valeriy Katkov


You can see how we solved the problem in ProveIt (see highlightLengthAtIndex). Basically, the trick is to truncate the textarea, scroll to the end, then restore the second part of the text. We also used the textSelection plugin for consistent cross-browser behavior.

like image 22
Matthew Flaschen Avatar answered Oct 30 '22 20:10

Matthew Flaschen


This is a code inspired by the Matthew Flaschen's answer.

/**
 * Scroll textarea to position.
 *
 * @param {HTMLInputElement} textarea
 * @param {Number} position
 */
function scrollTo(textarea, position) {
    if (!textarea) { return; }
    if (position < 0) { return; }

    var body = textarea.value;
    if (body) {
        textarea.value = body.substring(0, position);
        textarea.scrollTop = position;
        textarea.value = body;
    }
}

Basically, the trick is to truncate the textarea, scroll to the end, then restore the second part of the text.

Use it as follows

var textarea, start, end;
/* ... */

scrollTo(textarea, end);
textarea.focus();
textarea.setSelectionRange(start, end);
like image 29
naXa Avatar answered Oct 30 '22 22:10

naXa


Valeriy Katkov's elegant solution works great but has two problems:

  1. It does not work for long strings
  2. Selected contents are scrolled to the bottom of the textarea, making it hard to see the context which surrounds the selection

Here's my improved version that works for long strings (tested with at least 50,000 words) and scroll selection to the center of the textarea:

function setSelectionRange(textarea, selectionStart, selectionEnd) {
    // First scroll selection region to view
    const fullText = textarea.value;
    textarea.value = fullText.substring(0, selectionEnd);
    // For some unknown reason, you must store the scollHeight to a variable
    // before setting the textarea value. Otherwise it won't work for long strings
    const scrollHeight = textarea.scrollHeight
    textarea.value = fullText;
    let scrollTop = scrollHeight;
    const textareaHeight = textarea.clientHeight;
    if (scrollTop > textareaHeight){
        // scroll selection to center of textarea
        scrollTop -= textareaHeight / 2;
    } else{
        scrollTop = 0;
    }
    textarea.scrollTop = scrollTop;

    // Continue to set selection range
    textarea.setSelectionRange(selectionStart, selectionEnd);
}

It works in Chrome 72, Firefox 65, Opera 58, and Edge 42.

For an example of using this function, see my GitHub project SmartTextarea.

like image 43
AlienKevin Avatar answered Oct 30 '22 20:10

AlienKevin