Automatically resize textAreaInput in R shiny

I am attempting to adjust the technique outlined in this Stack Overflow answer on how to auto-resize a textarea input using JavaScript for shiny R. I prefer not to rely on additional packages like shinyJS.

Initially, I experimented with a pure JavaScript implementation by directly embedding the script into the application (approach 1). Subsequently, I explored triggering the JavaScript function from an observeEvent function within shiny (approach 2).

Regrettably, both approaches have failed to produce the desired outcome. It appears that some crucial element is missing.

Approach 1:

library(shiny)

jsCode1 <- "
            var observe;
            if (window.attachEvent) {
            observe = function (element, event, handler) {
            element.attachEvent('on'+event, handler);
            };
            }
            else {
            observe = function (element, event, handler) {
            element.addEventListener(event, handler, false);
            };
            }
            function init () {
            var text = document.getElementById('text');
            function resize () {
            text.style.height = 'auto';
            text.style.height = text.scrollHeight+'px';
            }
            /* 0-timeout to get the already changed text */
            function delayedResize () {
            window.setTimeout(resize, 0);
            }
            observe(text, 'change',  resize);
            observe(text, 'cut',     delayedResize);
            observe(text, 'paste',   delayedResize);
            observe(text, 'drop',    delayedResize);
            observe(text, 'keydown', delayedResize);

            text.focus();
            text.select();
            resize();
            }

            init();
            "

shinyApp(ui = 

           fluidPage(

                        tags$script(jsCode1),

                        tags$head(

                          tags$style("
                                     textarea {
                                     border: 0 none white;
                                     overflow: hidden;
                                     padding: 0;
                                     outline: none;
                                     background-color: #D0D0D0;
                                     }
                                     "
                          )

                          ),

                               shiny::tagAppendAttributes(
                                 textAreaInput(inputId = "text",
                                               label = "Enter text here",
                                               placeholder = "insert your text here",
                                               width = "100%"),
                                 style = "width: 100%;")

                        ),

         server = function(input, output, session) {

         }
                      )

Approach 2:

library(shiny)

jsCode2 <- "

            Shiny.addCustomMessageHandler('handler1', init);

            function init (el) {
            var text = document.getElementById(el);
            function resize () {
            text.style.height = 'auto';
            text.style.height = text.scrollHeight+'px';
            }
            /* 0-timeout to get the already changed text */
            function delayedResize () {
            window.setTimeout(resize, 0);
            }
            observe(text, 'change',  resize);
            observe(text, 'cut',     delayedResize);
            observe(text, 'paste',   delayedResize);
            observe(text, 'drop',    delayedResize);
            observe(text, 'keydown', delayedResize);

            text.focus();
            text.select();
            resize();
            }"

shinyApp(ui = 

           fluidPage(

                        tags$script(jsCode2),

                        tags$head(

                          tags$style("
                                     textarea {
                                     border: 0 none white;
                                     overflow: hidden;
                                     padding: 0;
                                     outline: none;
                                     background-color: #D0D0D0;
                                     }
                                     "
                          )

                          ),

                               shiny::tagAppendAttributes(
                                 textAreaInput(inputId = "text",
                                               label = "Enter text here",
                                               placeholder = "insert your text here",
                                               width = "100%"),
                                 style = "width: 100%;")

                        ),

         server = function(input, output, session) {

           observeEvent(input$text,{

             session$sendCustomMessage("handler1", message = "text")

           })

         }
                      )

Answer №1

One potential issue may arise if you attempt to link the resizing event to the textinput() before it is incorporated into the DOM.

To address this, I implemented an event listener that waits for the DOM to load prior to attaching the resizing event.

document.addEventListener('DOMContentLoaded', function(event) {...})

shinyjs will take care of this task automatically for you. By employing the above event listener, you can achieve the desired functionality without relying on shinyjs.

Javascript code:

jsCode <- "document.addEventListener('DOMContentLoaded', function(event) {
    var observe;
    if (window.attachEvent) {
      observe = function (element, event, handler) {
        element.attachEvent('on'+event, handler);
      };
    }
    else {
      observe = function (element, event, handler) {
        element.addEventListener(event, handler, false);
      };
    }
    function init () {
      var text = document.getElementById('text');
      function resize () {
        text.style.height = 'auto';
        text.style.height = text.scrollHeight+'px';
      }
      /* 0-timeout to get the already changed text */
        function delayedResize () {
          window.setTimeout(resize, 0);
        }
      observe(text, 'change',  resize);
      observe(text, 'cut',     delayedResize);
      observe(text, 'paste',   delayedResize);
      observe(text, 'drop',    delayedResize);
      observe(text, 'keydown', delayedResize);

      text.focus();
      text.select();
      resize();
    };init()
  })
"

The application:

library(shiny)

ui <- fluidPage(
  shiny::tags$script(jsCode),
  textAreaInput(inputId = "text", label = "a", value = "b")
)

server <- function(input, output, session) {
}

shinyApp(ui, server)

Answer №2

Although it may seem overdue, I found a solution that allows the textArea to dynamically expand vertically as you type:

library(shiny)

ui <- fluidPage(
  tags$head(
    tags$style(
      HTML("
        textArea {
          overflow: hidden;
          resize: none;
        }
      ")
    ),
    tags$script(
      HTML('
        $(document).on("input", "#text", function() {
          this.style.height = "auto";
          this.style.height = (this.scrollHeight) + "px";
        });
      ')
    )
  ),
  textAreaInput("text", "", placeholder = "Enter your text", rows = 1, width = "200px")
)

server <- function(input, output, session) {
  observeEvent(input$text,
               print(input$text)
            )
}

shinyApp(ui, server)

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

Tips for inserting items into an array of objects?

I have an array of objects with categories and corresponding points, and I need to calculate the total points for each category. { category: A, points:2 }, { category: A points: 3 }, { category: B, points: ...

Is there a way for me to generate a new column using a date condition?

There are two dataframes in my possession. The first one is structured as follows: time transactID itemID order salesPrice day 1: 2018-01-01 00:01:56 2278968 450 1 17.42 2018-01-01 2: 2018-01-01 00:01:56 227896 ...

Having difficulty implementing interval to a maximum of 2 minutes or until a certain condition is fulfilled in Angular

In my current scenario, I am working with two APIs - apiOne and apiTwo. When I call apiOne, it should return a response. If the response is successful, then I need to pass this response as a parameter to apiTwo. ApiTwo will then provide another response wh ...

JSP - Submitting a form through a link click: A complete guide

I am having an issue with submitting a form when clicking on a link. Despite my attempts, the default action is not being prevented and I keep getting the error message: HTTP Status 405 - Request method 'POST' not supported. Here's what I ha ...

RMarkdown, R Notebooks, and Optimal Memory Allocation

In the midst of tackling a project that involves analyzing a multitude of extensive text files, I've opted to break down the work into separate segments, each to be executed in their own RMarkdown/R Notebook. However, a few obstacles have surfaced. P ...

Retrieve information from an express server using the fetch API

I am attempting to use the alert function to display a variable string in Express's .get() method and then send it using res. I want the alert to show "I am working fetch". This is my server.js var express = require('express'); var app = e ...

Receiving the error message "Encountered issue reading property of 0" while attempting to retrieve a specific element from an array

Currently, I am facing an issue where I am trying to access a value that is set in one function within another function. When I attempt to return this value at the end, the console.log displays the value correctly. However, when I try to set it, I receive ...

The Bootstrap modal causes the scrollbar to vanish upon closure

Despite trying numerous solutions and hacks on this issue from StackOverflow, none of them seem to work for me. Currently, I am utilizing a modal for the login process in Meteor (e.g. using Facebook login service) which triggers a callback upon successful ...

I've come across this ajax url that seems promising, but I keep getting a GET 404 (Not Found)

I am attempting to validate using ajax and php. This is the code I have for my ajax: function PrintRecibopapel() { recibo = document.getElementById("txtCod").value; if(recibo == "") { alert("You must save the receipt before pr ...

Js: Automatically populating data into various input fields

I've encountered an issue with form input value injection using a <script> and POST requests. When I attempt to create another form with the same input field (same name and id), the value injection doesn't work, and troubleshooting has bee ...

Issue with updating the vertices in three.js EdgesGeometry is causing the Line Segments to not be updated as expected

I have created multiple three.js objects. I have observed that the 'other' objects, designed as Mesh/Geometry/Material, update as expected after calling verticesNeedUpdate() Furthermore, I have two wireframe objects that were designed in this m ...

Meteor: Simplifying the process of deleting two documents from separate collections in MongoDB with just one click

There is a possibility that some "news documents" (from one collection) may contain an image (from another collection I'm using cfs:standard-packages and cfs:filesystem for file handling). Here is an example of a news document in the Mongo Database: ...

Setting limits in HTML input values is quite simple and effective

Is there a way to create a form where each user has a limit on the quantity they can request from their database, and prevent them from requesting more than their limit using JavaScript? Maximum Allowed Quantity: <input type="number" value ...

"Utilizing Promises in AngularJS Factories for Synchronous API Calls

Attempting to implement synchronous calls using a factory pattern. $scope.doLogin = function (username, password, rememberme) { appKeyService.makeCall().then(function (data) { // data = JSON.stringify(data); debugAlert("logi ...

Validation of forms on the client side using Angular in a Rails application

I'm facing an issue with implementing client-side validations for a devise registration form using Angular. Although I am able to add the "invalid" class to the fields as expected, I am struggling to get any output when using ng-show. There are no oth ...

Leveraging the mutate function to generate new columns based on existing column values

In the given dataset, I want to add new columns based on the values in the "Type" column by using 'mutate' and then count the occurrences. The data should be grouped according to "Group" and "Choice". As the "Type" column may have additional val ...

How can I prevent text highlighting on a website?

Is there a way to lock the copy button on my site without restricting the save as button, which is activated by right click? I want users to be able to save the website as an HTML file, but prevent them from copying text. Can this be achieved using Javas ...

"Div does not perfectly match the height of its parent div when using the 'inherit' value

I have 3 divs inside a parent div, intended to be arranged like so: ______________________________________ | | | HEADER | |______________________________________| ____ ________________ ...

After clicking, revert back to the starting position

Hey there, I have a question about manipulating elements in the DOM. Here's the scenario: when I click a button, a div is displayed. After 2 seconds, this div is replaced by another one which has a function attached to it that removes the div again. ...

Tips for sharing an Environment Variable with the frontend in Next.js

The Challenge Trying to pass an environment variable to the front end in order to display a map from MapBox. My Approach I placed the API Key at the root of my directory: //.env.local MAPBOX_KEY="abc123" On the front-end, I attempted to acce ...