Search code examples
c#blazorblazor-client-side

Growing TextArea in Blazor


I need a text area that grows as the number of lines increases. As soon as lines are deleted, the TextArea should shrink again. In the best case with a maximum height.

How it works with Javascript I could already read here: textarea-to-resize-based-on-content-length

But in Blazor I have unfortunately, as far as I know, no "scrollHeight" available for the TextArea.

(My problem relates to the Blazor framework, which allows browser frontend development using C# (+HTML/CSS) and not a desktop UI like WPF/WinForms.)


Solution

  • As long as you know how many lines are in your text, you can just use the "rows" attribute on the TextView, like this

    <textarea rows="@Rows"
              @bind-value="MyText"
              @bind-value:event="oninput" />
    

    And in your code, you can determine the value for Rows

    Note, I use Math.Max(Rows,2) to keep a minimum of two rows.

    private void CalculateSize(string value)
    {
      Rows = Math.Max(value.Split('\n').Length, value.Split('\r').Length);
      Rows = Math.Max(Rows, 2);
    }
    

    I call CalculateSize from code that handles changes to "MyText" - either a custom setter like this or another method

    string _myText;
    protected string MyText
    {
      get => _myText;
      set
      {
        _myText = value; 
        CalculateSize(value);
      }
    }
    

    The maximum height can easily be set either through CSS for a design approach or by adding another constraint to the CalculateSize method.

    private void CalculateSize(string value)
    {
      Rows = Math.Max(value.Split('\n').Length, value.Split('\r').Length);
      Rows = Math.Max(Rows, MIN_ROWS);
      Rows = Math.Min(Rows, MAX_ROWS);
    }
    

    Option 2

    If you want simplicity and don't mind a bit of inline JS (if you do, then it's time to crack open the JSInterop....)

    <textarea 
          rows="2" 
          placeholder="Sample text."
          style="resize:both;"
          oninput="this.style.height = 'auto'; this.style.height = (this.scrollHeight) + 'px';">
    </textarea>
    

    Option 3

    If you did want to use JSInterop, you could do something like this or place your code in a JS file and include it in the page.

    <textarea id="MyTextArea"
          rows="2" 
          placeholder="Sample text."
          @oninput="Resize"></textarea>
    
    <label>This area is @(MyHeight)px</label>
    @code
    {
    [Inject] IJSRuntime JSRuntime { get; set; }
    double MyHeight=0;
    async Task Resize()
    {
        var result = await JSRuntime.InvokeAsync<object>("eval",@"(function() {
                MyTextArea.style.height='auto';
                MyTextArea.style.height=(MyTextArea.scrollHeight)+'px';
                return MyTextArea.scrollHeight;
            })()");
        Double.TryParse(result.ToString(), out MyHeight);
    }
    }