Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blurred input refocusing when turbo replaces blurred input #1248

Open
ajsharp opened this issue Apr 19, 2024 · 2 comments
Open

Blurred input refocusing when turbo replaces blurred input #1248

ajsharp opened this issue Apr 19, 2024 · 2 comments

Comments

@ajsharp
Copy link

ajsharp commented Apr 19, 2024

I'm running into an issue where I'm using a stimulus controller to submit a form based on an input's blur event, and then replacing that form using turbo frames. The problem is when the element is replaced, it refocuses the input that was just blurred.

What appears to be happening is:

  1. Input is blurred, triggering a stimulus action (form submission)
  2. A POST request is submitted via stimulus using fetch (requesting a turbo frame response)
  3. The server responds with a turbo frame
  4. The original dom element is replaced/updated (I've tested both replace and update, both have the same effect)
  5. The form input is re-focused

Note: on the screen on which the answers/_form partial is rendered, many instances of this partial are rendered, which means there are many stimulus controllers, forms and turbo frames being update. In other words, there is one html form per Answer instance, if that makes sense. I don't see why that would cause this behavior, but thought I'd mention it.

In this video, I'm tabbing out of a field to trigger the blur event, but same thing happens for other methods of blurring.

Test.video.mp4

Here's what I'm doing:

# answer_controller.js
import { Controller } from "@hotwired/stimulus";
import { post } from "@rails/request.js";

export default class extends Controller {
  connect() {
    this.form = this.element.closest("form");
  }

  submit(e) {
    const body = JSON.stringify({
      value: this.element.value,
    });

    // Set the responseKind to turbo-stream to properly wire up the turbo streams stuff in the controller.
    post(this.form.action, { body, responseKind: "turbo-stream" })
      .then((response) => {
        // handle response here
      })
      .catch((error) => {
        // handle error here
      });
  }
}
# create.turbo_stream.erb
<%= turbo_stream.update(
  "#{dom_id(@answer.question)}-answer",
  partial: 'answers/form',
  locals: { question: @answer.question, answer: @answer }) %>


# answers_controller.rb
class AnswersController < ApplicationController
  before_action :authenticate_user!

  # POST /questions/:question_id/answers
  def create
    @question = Question.find(params[:question_id])
    @answer = @question.answer_for(current_user)

    @answer.value = params[:value]
    if @answer.save
      respond_to do |format|
        format.turbo_stream
      end
    else
      # handle errors
    end
  end
end


# answers/_form.html.erb
<%= turbo_frame_tag "#{dom_id(question)}-answer", class: "answer-form" do %>
  <%= form_with model: question,
    url: question_answers_path(question),
    method: :post do |form| %>
    <%= form.label question, question.title %>

    <%= form.text_field :value,
      class: "#{"saved" if answer.saved}",
      data: {
        controller: "answer",
        action: 'blur->answer#submit'
    } %>
  <% end %>
<% end %>
@pinzonjulian
Copy link

it refocuses the input that was just blurred.

I believe there is a conceptual gap here. Visually, it's appears as if nothing happened, but what's really happening is that the old DOM node is ditched entirely and a new one is put in place. There is no "input that was just blurred" because the whole thing was replaced. In other words, you're expecting Turbo to remember the position of something after it's being "destroyed".

What you probably want in this case is to use morphing. Give this whole page a read and you may find a way to fix your issue.

https://turbo.hotwired.dev/handbook/page_refreshes.html#turbo-frames

Also, you can also make a better use of turbo and stimulus. This might help:

This seems to not be an issue with the library. I recommend closing this issue and asking for support in the community forum at https://discuss.hotwired.dev

@ajsharp
Copy link
Author

ajsharp commented May 1, 2024

I get what you're saying, but the input is blurred -- that's what kicks off the submit that creates a turbo response that replaces the element. I don't think I'm expecting turbo to remember UI state (I've seen a few issues related to that, this seems like the opposite) -- in effect I'm wanting turbo to respect the DOM event that already happened. It seems like what's happening is it's resetting the DOM state to try to keep what it thinks the state was before it did the replace.

Page morphing seems to be thing with whole page refreshes -- I'm updating a specific part of the dom with turbo frames in response to an xhr request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants