Capture Keyboard Input(barcode scanner) without input form in Liveview

I’m trying to build a Liveview with 2 parts:

  • top:
    • switch button: camera / hardware scanner
      • show camera scanner
      • or empty
  • bottom:
    • scan history table from db with pubsub: statistics, colors, time,…

When the hardware scanner is used and a scan is made, a sequence of characters is send ending with an Enter-key.

defmodule HelloWeb.ScanLive do
  use HelloWeb, :live_view

  def render(assigns) do
    <div phx-window-keyup="scanner">

  def handle_event("scanner", key, socket) do
    {:noreply, socket}

Is this the proper way to do this in Phoenix Liveview using handle_event with keyup?
Can you capture a sequence+Enter at once or do you have to capture separately and build it up.


If you want to capture each key individually, yes.

This requires a form input. I am curious why specifically you wish to avoid this. I can give you a working example (using an ancient Symbol LS1900 barcode scanner to demonstrate :slight_smile: ):

Here’s the code:

defmodule HelloWeb.ScanLive do
  use HelloWeb, :live_view

  def render(assigns) do
    <form id="form" phx-submit={JS.push("capture") |> JS.push_focus(to: "#input")}>
        placeholder="Scan barcode..."
      <button class="hidden" type="submit">Submit</button>

      <li :for={code <- @codes}><%= code %></li>

  def mount(_, _, socket) do
    socket = socket |> assign(:count, 0) |> assign(:codes, [])
    {:ok, socket}

  def handle_event("capture", %{"code" => code}, socket) do

    socket =
      |> update(:count, &(&1 + 1))
      |> update(:codes, &[code | &1])

    {:noreply, socket}

Usually there are (at least) two tricky parts. The first is clearing and re-focusing the input after the form submits. You can use a combination of JS.push() and JS.push_focus() to submit the form and re-focus the input. To ensure the input is cleared after submit, you need to force the input to re-render. In this case I am setting a data-count attribute and incrementing the count on each submit. This will force LiveView to re-render the input with the empty value provided on the element.

The other tricky part is that barcode scanners don’t always send an Enter key as a suffix. I left the phx-debounce="blur" on the input because you will definitely want that if you try to add a phx-change event to the form. Since LiveView won’t re-render focused inputs on change, you would probably want to push an event from the server and use it to reset the input value.


that’s so cool!

1 Like

Thank you. I’ve tried similar with a form & changeset & onfocus: "this.value=''"'to reset the value. Your code is much cleaner though.

I have currently 2 reasons. I’m testing with an android phone with 2d scanner (Blovedream U9000)

  • When the focus is on the input the software keyboard keeps popping up. I though when there is no input, there would be no software keyboard filling half the screen.
    Edit: Adding inputmode="none" to input fixes this.

  • When the user clicks outside the input, the focus is lost. Scans are not captured until you refocus on the input.

I’ve also added autocomplete="off" to input

For capturing a sequence+Enter at once, I’m not sure if that’s possible. It might be easier to capture the sequence and then add the Enter key as a separate event.
Regarding your issue with the software keyboard popping up, adding inputmode=“none” to the input should fix that.