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!

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