Smooth Transitions with Turbo Streams
This article was originally published on Rails Designer
With Turbo Streams you can update specific parts of your app. Inject a chat message, update a profile picture or insert a Report is being created alert.
The preciseness Turbo Streams offers is great. But often the abruptness of it, its not too appealing to me. The new component is just there (or isn't, if you remove it).
I'd like to add a bit more joy to my apps and this technique is something that does just that. I previously explored multiple techniques to add some kind of transition or animation when an element was inserted or removed. I fine-tuned it over the years while using it in production. And I can say I'm happy with how the technique works I am outlining today.
First, this will be the end result:
And this is how it's used:
<%= turbo_stream_action_tag_with_block "prepend", target: "resources", data: {transition_enter: "transition ease-in duration-300", transition_enter_start: "opacity-0", transition_enter_end: "opacity-100"} do %> <%= render ResourceComponent.new(resource: @resource) %> <% end %>
If you are using Rails Designer it is included for you and ready to go. Winning! ?
There are quite a few moving elements here to get it going, but as seen from the code example above, the usage is really clean and customizable.
Let's get started.
The first step is to create the turbo_stream_action_tag_with_block helper. This is needed, because the available turbo_stream_action_tag helper doesn't allow a block to be passed (eg. render CollectionComponent) and the the default Turbo stream Rails helpers don't pass along data-attributes. It's simple enough though:
# app/helpers/turbo_stream_helper.rb module TurboStreamHelper def turbo_stream_action_tag_with_block(action, target: nil, targets: nil, **options, &block) template_content = block_given? ? capture(&block) : nil turbo_stream_action_tag(action, target: target, targets: targets, template: template_content, **options) end end
Next up is to add a listener for turbo:before-stream-render and add some custom behavior.
// app/javascript/utilities/turbo_stream_render.js document.addEventListener("turbo:before-stream-render", (event) => { const { target } = event if (!(target.firstElementChild instanceof HTMLTemplateElement)) return const { dataset, templateElement } = target const { transitionEnter, transitionLeave } = dataset if (transitionEnter !== undefined) { transitionEnter(event, templateElement, dataset) } if (transitionLeave !== undefined) { handleTransitionLeave(event, target, dataset) } }) const handleTransitionEnter = (event, templateElement, dataset) => { event.preventDefault() const firstChild = templateElement.content.firstElementChild Object.assign(firstChild.dataset, dataset) firstChild.setAttribute("hidden", "") firstChild.setAttribute("data-controller", "appear") event.target.performAction() } const handleTransitionLeave = (event, target, dataset) => { const leaveElement = document.getElementById(target.target) if (!leaveElement) return event.preventDefault() Object.assign(leaveElement.dataset, dataset) leaveElement.setAttribute("data-controller", "disappear") }
Wow! This looks scary! It's not too bad really! It intercepts the turbo:before-stream-render event, checking the target element's dataset for specific transition attributes (eg. data-transition-start). For entering elements, it sets them to hidden and adds an appear data-controller. For leaving elements, it adds a disappear data-controller.
? Want to be more comfortable writing and understanding JavaScript as a Ruby on Rails developer? Check out the book JavaScript for Rails Developers
Make sure to import it into your application.js.
// app/javascript/application.js import "@hotwired/turbo-rails" import "./controllers" import "./utilities/turbo_stream_render.js" // …
Let's create those two controllers now. They are really simple and rely on the cool el-transition library. Make sure to add it to your app (either via NPM or importmaps).
// app/javascript/controllers/appear_controller.js import ApplicationController from "./application_controller" import { enter } from "el-transtion" export default class extends ApplicationController { connect() { enter(this.element) } }
// app/javascript/controllers/disappear_controller.js import ApplicationController from "./application_controller" import { leave } from "el-transtion" export default class extends ApplicationController { connect() { leave(this.element).then(() => { this.element.remove() }) } }
And with that all out of the way, you can now add smooth transitions to any added or removed element using turbo streams.
Simply append some data-attributes (data: {transition_enter: ""}) to the turbo stream and enjoy a smooth ride.
You can use the same data-attributes supported by el-transition:
- data-transition-enter;
- data-transition-enter-start;
- data-transition-enter-end;
- data-transition-leave;
- data-transition-leave-start;
- data-transition-leave-end.
For adding elements:
<%= turbo_stream_action_tag_with_block "prepend", target: "resources", data: {transition_enter: "transition ease-in duration-300", transition_enter_start: "opacity-0", transition_enter_end: "opacity-100"} do %> <%= render ResourceComponent.new(resource: @resource) %> <% end %>
And for removing elements:
<%= turbo_stream_action_tag_with_block "remove", target: dom_id(@resource), data: {transition_leave: "transition ease-in duration-300", transition_leave_start: "opacity-100", transition_leave_end: "opacity-0"} %>
The above is the detailed content of Smooth Transitions with Turbo Streams. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

Frequently Asked Questions and Solutions for Front-end Thermal Paper Ticket Printing In Front-end Development, Ticket Printing is a common requirement. However, many developers are implementing...

JavaScript is the cornerstone of modern web development, and its main functions include event-driven programming, dynamic content generation and asynchronous programming. 1) Event-driven programming allows web pages to change dynamically according to user operations. 2) Dynamic content generation allows page content to be adjusted according to conditions. 3) Asynchronous programming ensures that the user interface is not blocked. JavaScript is widely used in web interaction, single-page application and server-side development, greatly improving the flexibility of user experience and cross-platform development.

There is no absolute salary for Python and JavaScript developers, depending on skills and industry needs. 1. Python may be paid more in data science and machine learning. 2. JavaScript has great demand in front-end and full-stack development, and its salary is also considerable. 3. Influencing factors include experience, geographical location, company size and specific skills.

Learning JavaScript is not difficult, but it is challenging. 1) Understand basic concepts such as variables, data types, functions, etc. 2) Master asynchronous programming and implement it through event loops. 3) Use DOM operations and Promise to handle asynchronous requests. 4) Avoid common mistakes and use debugging techniques. 5) Optimize performance and follow best practices.

Discussion on the realization of parallax scrolling and element animation effects in this article will explore how to achieve similar to Shiseido official website (https://www.shiseido.co.jp/sb/wonderland/)...

How to merge array elements with the same ID into one object in JavaScript? When processing data, we often encounter the need to have the same ID...

The latest trends in JavaScript include the rise of TypeScript, the popularity of modern frameworks and libraries, and the application of WebAssembly. Future prospects cover more powerful type systems, the development of server-side JavaScript, the expansion of artificial intelligence and machine learning, and the potential of IoT and edge computing.

In-depth discussion of the root causes of the difference in console.log output. This article will analyze the differences in the output results of console.log function in a piece of code and explain the reasons behind it. �...
