Home > front end >  How to create a controlled (with React useState) SVG text field?
How to create a controlled (with React useState) SVG text field?

Time:11-14

<div contentEditable>
  <svg viewBox='0 0 300 300'>
    <text x=2 y=40>
      Can this be a text input field?
    </text>
    <text y=80>
      But not this
    </text>
    <text y=110>
      And not this
    </text>
  </svg>
</div>

The text in the above SVG is editable because it is wrapped in <div contentEditable>, however for our use case this is not ideal as is:

  • most importantly, we are using React and we'd like the editable text to be controlled in state.
  • all text in the above SVG is editable, not just the first text field as is desired
  • it is a big buggy, with the cursor going way far to the right

It is possible to create an individually controlled (with React useState) SVG text field?

EDIT: something along these lines does appear to work:

const [test, setTest] = useState('here is some text');

// And Return
return (<>
    <svg>
        <foreignObject x="40" y="40" width="300" height="150">
            <div xmlns="http://www.w3.org/1999/xhtml">
                <FormControl
                    className='modal-input'
                    type='text'
                    value={test}
                    onChange={(e) => setTest(e.target.value)}
                    name='firstname'
                    placeholder='First Name'
                />
            </div>
        </foreignObject>
        ...

Where FormElement is from react-bootstrap. We are going to keep working with this and see if it fits our use case fully, but so far, so good!

CodePudding user response:

You can embed HTML fragments (including React portals for example) in SVG using the <foreignObject> tag (which is specifically designed to allow HTML in SVG). BTW I think if you just want to embed some inputs into SVG, doing it using contenteditable does not seem to make any sense. Just use normal <inputs>

<svg viewBox='0 0 300 300'>
  <foreignObject x='0' y='0' width='100' height='100'>
    <div contenteditable>
      Can this be a text input field?
    </div>
  </foreignObject>
  <text y='80'>
    But not this
  </text>
  <text y='110'>
    And not this
  </text>
</svg>
  • Related