nanojsx / nano

🎯 SSR first, lightweight 1kB JSX library.
http://nanojsx.io
MIT License
1.45k stars 38 forks source link

inputs lose focus on... well, input #105

Closed ghost closed 2 years ago

ghost commented 2 years ago

Description Whenever I type into an input-element (like the one below), the input loses focus, and I have to re-click to type on.

here's an example of what I'm doing:

<input
  type="text"
  value={this.state.message}
  onInput={(event: any) => this.setState({ message: event.target.value }, true)}
/>

Am I doing something wrong, or is this sort of thing supposed to work?

jrson83 commented 2 years ago

this.setState({ message: event.target.value }, true)}

With setState function to true, you call an update on the component, everytime an input event (keypress) is fired. When the component updates, you lose the focus (check the Component docs for more info).

One workaround would be to set the event.target.value not on keypress, but when the input loses focus (user clicks somewhere else). Or like in this example, at the moment, when pressing submit.

yandeu commented 2 years ago

I recommend you follow the form example: https://nanojsx.io/examples.html#form

When working with forms, I prefere to use vanilla javascript to update the DOM instead of calling update()

ghost commented 2 years ago

When the component updates, you lose the focus

This isn't the sort of approach React or similar libraries take - Do you intend to in the future, or do you consider this a non-feature - in a sentence: to maintain element-focus across DOM-updates?

yandeu commented 2 years ago

I don't think this is something I will add. The input element will be replaced once calling update(), therefore it is not possible to remain focused.

Consider something like this, without ever re-rendering the component:

class App extends Component {
  didMount() {
    this.renderMessage()
  }

  renderMessage(length = 0) {
    const minimum = 8
    render(
      <p>
        Min. characters needed is {minimum} ({length - minimum})
      </p>,
      document.getElementById('message')
    )
  }

  validate(event: any) {
    const el = event.currentTarget as HTMLInputElement

    if (el.value.length >= 8) el.classList.add('ok')
    else if (el.value.length > 0) el.classList.add('warn')
    else {
      el.classList.remove('warn')
      el.classList.remove('ok')
    }

    this.renderMessage(el.value.length || 0)
  }

  render() {
    return (
      <form>
        <div id="message"></div>
        <style>{'.warn { background-color: orange; }.ok { background-color: lightgreen; }'}</style>
        <input type="text" onInput={this.validate.bind(this)} />
      </form>
    )
  }
}

render(<App />, document.getElementById('root'))
ghost commented 2 years ago

thanks for you feedback 👍

I come at this from the perspective of programming in the browser using Elm, React, etc.

I'm excited about doing this sort of work without having to rely on the DOM directly - I appreciate the minimalism of nanojsx, and I'd love to be able to do state-binding of inputs and component-state (like my example above).

Thanks for building this - it's really cool.

yandeu commented 2 years ago

You're welcome 😁

Maybe you could write a custom <Input/> component? Or a functionality to update the value of each input field without triggerng a re-render? There are alternatives to two-way bindings. 👍🏻

ghost commented 2 years ago

One of the reasons I really enjoy Elm is the idiom of having all state in one place, and reducing over that when rendering graphics.

The programming-model in Nano requires manually programming state-synchronization between my application-model, and the input itself - in other words:

  1. code to mutate the DOM, and update the input value... Ideally within the Input-element. This would require it to be a Component with lifecycle-methods, instead of a pure view-function
  2. som sort of onInput event-handler to pass the value back from Input, into my application-model

There's probably other ways around this, but with the sort of architecture I have in mind... I imagine something along those lines to be the idiomatic NanoJSX-approach?

ghost commented 2 years ago

being close to the DOM is really beneficial in a lot of cool ways - I really like that. But it breaks the abstraction of not having a DOM at all, which is my preferred method 😄