Search code examples
cssreactjsstyled-components

React: how to make an input only as wide as the amount of text provided?


Simple enough question: I am trying to create inputs that are as large as the text supplied to them.

Sandbox: https://codesandbox.io/s/long-snowflake-6u13n?file=/src/Test.jsx

My design intention is to generate inputs dynamically and then allow the user to have styles specific to each input that visually help break up each sentence based on outside events. But before I can move forward, it's really important that my input container is only as large as the text within.

why not use a textarea? -- I have data that is particular to each sentence that I want to create unique styles for.

Any thoughts?


Solution

  • Here is an approach from plain HTML/CSS and a working snippet , hidding the value typed inside a span behind the input set in an absolute position. CSS can make both span and input matching the same lenght/width. Stretching/collapsing a parent (label) will finish the job.

    In the courtesy of @silvenon you may also find a react sample below the snippet

    var val = document.querySelector('#test');
    let tpl = document.querySelector('#tpl');
    let text = val.value;
     tpl.textContent= text;
    
    val.addEventListener("input", function () {// onchange ...
      let text= val.value;
      //console.log(text);
      tpl.textContent= text;
      });
    label {
      display: inline-block;
      position: relative;
      min-width: 2em;
      min-height: 1.4em;
    }
    
    #tpl {
      white-space: pre;
      /* max-width : could be wised to set a maximum width and overflow:hidden; */
    }
    
    #test {
      font-family: inherit;
      font-size: inherit;
      position: absolute;
      vertical-align: top;
      top: 0;
      left: 0;
      width: 100%;
      background: white;
    }
    <label><span id="tpl"></span><input id='test' value="Some test to try" ></label>

    In the courtesy of @silvenon, you may find a react sample of that code.

    const SentenceInput = styled.input`
      padding: 0;
      margin: 0;
      border: none;
      border: 1px solid black;
      /* added styles */
      font-family: inherit;
      font-size: inherit;
      position: absolute;
      vertical-align: top;
      top: 0;
      left: 0;
      width: 100%;
      background: white;
    `
    
    const Label = styled.label`
      display: inline-block;
      position: relative;
      min-width: 2em;
      min-height: 1.4em;
    `
    
    const Template = styled.span`
      white-space: pre;
      /* max-width : could be wised to set a maximum width and overflow:hidden; */
    `
    
    const Sentence = ({ initialValue }) => {
      const [value, setValue] = React.useState(initialValue)
      return (
        <Label>
          <Template>{value}</Template>
          <SentenceInput
            type="text"
            value={value}
            onChange={(event) => {
              setValue(event.target.value)
            }}
          />
        </Label>
      )
    }