1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Merge pull request #43957 from DavidColby/update-javascript-guide-for-rails-7

Update Working With JavaScript guide for Rails 7, scrap Node/Yarn from Getting Started guide
This commit is contained in:
Gannon McGibbon 2022-02-11 14:33:20 -05:00 committed by GitHub
commit 761a2e2552
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 195 additions and 509 deletions

View file

@ -83,8 +83,6 @@ proper prerequisites installed. These include:
* Ruby
* SQLite3
* Node.js
* Yarn
#### Installing Ruby
@ -121,31 +119,6 @@ $ sqlite3 --version
The program should report its version.
#### Installing Node.js and Yarn
Finally, you'll need Node.js and Yarn installed to manage your application's JavaScript.
Find the installation instructions at the [Node.js website](https://nodejs.org/en/download/) and
verify it's installed correctly with the following command:
```bash
$ node --version
```
The version of your Node.js runtime should be printed out. Make sure it's greater
than 8.16.0.
To install Yarn, follow the installation
instructions at the [Yarn website](https://classic.yarnpkg.com/en/docs/install).
Running this command should print out Yarn version:
```bash
$ yarn --version
```
If it says something like "1.22.0", Yarn has been installed correctly.
#### Installing Rails
To install Rails, use the `gem install` command provided by RubyGems:

View file

@ -3,561 +3,274 @@
Working with JavaScript in Rails
================================
This guide covers the built-in Ajax/JavaScript functionality of Rails (and
more); it will enable you to create rich and dynamic Ajax applications with
ease!
This guide covers the options for integrating JavaScript functionality into your Rails application,
including the options you have for using external JavaScript packages and how to use Turbo with
Rails.
After reading this guide, you will know:
* The basics of Ajax.
* Unobtrusive JavaScript.
* How Rails' built-in helpers assist you.
* How to handle Ajax on the server side.
* The Turbolinks gem.
* How to include your Cross-Site Request Forgery token in request headers
* How to use Rails without the need for a Node.js, Yarn, or a JavaScript bundler.
* How to create a new Rails application using import maps, esbuild, rollup, or webpack to bundle
your JavaScript.
* What Turbo is, and how to use it.
* How to use the Turbo HTML helpers provided by Rails.
-------------------------------------------------------------------------------
--------------------------------------------------------------------------------
An Introduction to Ajax
------------------------
Import maps
-----------
In order to understand Ajax, you must first understand what a web browser does
normally.
[Import maps](https://github.com/rails/importmap-rails) let you import JavaScript modules using
logical names that map to versioned files directly from the browser. Import maps are the default
from Rails 7, allowing anyone to build modern JavaScript applications using most NPM packages
without the need for transpiling or bundling.
When you type `http://localhost:3000` into your browser's address bar and hit
'Go', the browser (your 'client') makes a request to the server. It parses the
response, then fetches all associated assets, like JavaScript files,
stylesheets and images. It then assembles the page. If you click a link, it
does the same process: fetch the page, fetch the assets, put it all together,
show you the results. This is called the 'request response cycle'.
Applications using import maps do not need [Node.js](https://nodejs.org/en/) or
[Yarn](https://yarnpkg.com/) to function. If you plan to use Rails with `importmap-rails` to
manage your JavaScript dependencies, there is no need to install Node.js or Yarn.
JavaScript can also make requests to the server, and parse the response. It
also has the ability to update information on the page. Combining these two
powers, a JavaScript writer can make a web page that can update just parts of
itself, without needing to get the full page data from the server. This is a
powerful technique that we call Ajax.
When using import maps, no separate build process is required, just start your server with
`bin/rails server` and you are good to go.
As an example, here's some JavaScript code that makes an Ajax request:
### Adding NPM Packages with importmap-rails
```js
fetch("/test")
.then((data) => data.text())
.then((html) => {
const results = document.querySelector("#results");
results.insertAdjacentHTML("beforeend", html);
});
To add new packages to your import map-powered application, run the `bin/importmap pin` command
from your terminal:
```bash
$ bin/importmap pin react react-dom
```
This code fetches data from "/test", and then appends the result to the element
with an id of `results`.
Rails provides quite a bit of built-in support for building web pages with this
technique. You rarely have to write this code yourself. The rest of this guide
will show you how Rails can help you write websites in this way, but it's
all built on top of this fairly simple technique.
Unobtrusive JavaScript
----------------------
Rails uses a technique called "Unobtrusive JavaScript" to handle attaching
JavaScript to the DOM. This is generally considered to be a best-practice
within the frontend community, but you may occasionally read tutorials that
demonstrate other ways.
Here's the simplest way to write JavaScript. You may see it referred to as
'inline JavaScript':
```html
<a href="#" onclick="this.style.backgroundColor='#990000';event.preventDefault();">Paint it red</a>
Then, import the package into `application.js` as usual:
```javascript
import React from "react"
import ReactDOM from "react-dom"
```
When clicked, the link background will become red. Here's the problem: what
happens when we have lots of JavaScript we want to execute on a click?
Adding NPM Packages with JavaScript Bundlers
--------
```html
<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';event.preventDefault();">Paint it green</a>
Import maps are the default for new Rails applications, but if you prefer traditional JavaScript
bundling, you can create new Rails applications with your choice of
[esbuild](https://esbuild.github.io/), [webpack](https://webpack.js.org/), or
[rollup.js](https://rollupjs.org/guide/en/).
To use a bundler instead of import maps in a new Rails application, pass the `—javascript` or `-j`
option to `rails new`:
```
$ rails new my_new_app --javascript=webpack
OR
$ rails new my_new_app -j webpack
```
Awkward, right? We could pull the function definition out of the click handler,
and turn it into a function:
These bundling options each come with a simple configuration and integration with the asset
pipeline via the [jsbundling-rails](https://github.com/rails/jsbundling-rails) gem.
```js
window.paintIt = function(event, backgroundColor, textColor) {
event.preventDefault();
event.target.style.backgroundColor = backgroundColor;
if (textColor) {
event.target.style.color = textColor;
}
}
When using a bundling option, use `bin/dev` to start the Rails server and build JavaScript for
development.
### Installing Node.js and Yarn
If you are using a JavaScript bundler in your Rails application, Node.js and Yarn must be
installed.
Find the installation instructions at the [Node.js website](https://nodejs.org/en/download/) and
verify its installed correctly with the following command:
```bash
$ node --version
```
And then on our page:
The version of your Node.js runtime should be printed out. Make sure its greater than `8.16.0`.
```html
<a href="#" onclick="paintIt(event, '#990000')">Paint it red</a>
To install Yarn, follow the installation instructions at the
[Yarn website](https://classic.yarnpkg.com/en/docs/install). Running this command should print out
the Yarn version:
```bash
$ yarn --version
```
That's a little bit better, but what about multiple links that have the same
effect?
If it says something like `1.22.0`, Yarn has been installed correctly.
```html
<a href="#" onclick="paintIt(event, '#990000')">Paint it red</a>
<a href="#" onclick="paintIt(event, '#009900', '#FFFFFF')">Paint it green</a>
<a href="#" onclick="paintIt(event, '#000099', '#FFFFFF')">Paint it blue</a>
```
Choosing Between Import Maps and a JavaScript Bundler
-----------------------------------------------------
Not very DRY, eh? We can fix this by using events instead. We'll add a `data-*`
attribute to our link, and then bind a handler to the click event of every link
that has that attribute:
When you create a new Rails application, you will need to choose between import maps and a
JavaScript bundling solution. Every application has different requirements, and you should
consider your requirements carefully before choosing a JavaScript option, as migrating from one
option to another may be time-consuming for large, complex applications.
```js
function paintIt(element, backgroundColor, textColor) {
element.style.backgroundColor = backgroundColor;
if (textColor) {
element.style.color = textColor;
}
}
Import maps are the default option because the Rails team believes in import maps' potential for
reducing complexity, improving developer experience, and delivering performance gains.
window.addEventListener("load", () => {
const links = document.querySelectorAll(
"a[data-background-color]"
);
links.forEach((element) => {
element.addEventListener("click", (event) => {
event.preventDefault();
For many applications, especially those that rely primarily on the [Hotwire](https://hotwired.dev/)
stack for their JavaScript needs, import maps will be the right option for the long term. You
can read more about the reasoning behind making import maps the default in Rails 7
[here](https://world.hey.com/dhh/rails-7-will-have-three-great-answers-to-javascript-in-2021-8d68191b).
const {backgroundColor, textColor} = element.dataset;
paintIt(element, backgroundColor, textColor);
});
});
});
```
```html
<a href="#" data-background-color="#990000">Paint it red</a>
<a href="#" data-background-color="#009900" data-text-color="#FFFFFF">Paint it green</a>
<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a>
```
Other applications may still need a traditional JavaScript bundler. Requirements that indicate
that you should choose a traditional bundler include:
We call this 'unobtrusive' JavaScript because we're no longer mixing our
JavaScript into our HTML. We've properly separated our concerns, making future
change easy. We can easily add behavior to any link by adding the data
attribute. We can run all of our JavaScript through a minimizer and
concatenator. We can serve our entire JavaScript bundle on every page, which
means that it'll get downloaded on the first page load and then be cached on
every page after that. Lots of little benefits really add up.
* If your code requires a transpilation step, such as JSX or TypeScript.
* If you need to use JavaScript libraries that include CSS or otherwise rely on
[Webpack loaders](https://webpack.js.org/loaders/).
* If you are absolutely sure that you need
[tree-shaking](https://webpack.js.org/guides/tree-shaking/).
* If you will install Bootstrap, Bulma, PostCSS, or Dart CSS through the
[cssbundling-rails gem](https://github.com/rails/cssbundling-rails). All options provided by this
gem except Tailwind will automatically install `esbuild` for you if you do not specify a different
option in `rails new`.
Built-in Helpers
----------------
Turbo
-----
### Remote Elements
Whether you choose import maps or a traditional bundler, Rails ships with
[Turbo](https://turbo.hotwired.dev/) to speed up your application while dramatically reducing the
amount of JavaScript that you will need to write.
Rails provides a bunch of view helper methods written in Ruby to assist you
in generating HTML. Sometimes, you want to add a little Ajax to those elements,
and Rails has got your back in those cases.
Turbo lets your server deliver HTML directly as an alternative to the prevailing front-end
frameworks that reduce the server-side of your Rails application to little more than a JSON API.
Because of Unobtrusive JavaScript, the Rails "Ajax helpers" are actually in two
parts: the JavaScript half and the Ruby half.
### Turbo Drive
Unless you have disabled the Asset Pipeline,
[rails-ujs](https://github.com/rails/rails/tree/main/actionview/app/assets/javascripts)
provides the JavaScript half, and the regular Ruby view helpers add appropriate
tags to your DOM.
[Turbo Drive](https://turbo.hotwired.dev/handbook/drive) speeds up page loads by avoiding full-page
teardowns and rebuilds on every navigation request. Turbo Drive is an improvement on and
replacement for Turbolinks.
You can read below about the different events that are fired dealing with
remote elements inside your application.
### Turbo Frames
#### form_with
[Turbo Frames](https://turbo.hotwired.dev/handbook/frames) allow predefined parts of a page to be
updated on request, without impacting the rest of the pages content.
[`form_with`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_with)
is a helper that assists with writing forms. To use Ajax for your form you can
pass the `:local` option to `form_with`.
You can use Turbo Frames to build in-place editing without any custom JavaScript, lazy load
content, and create server-rendered, tabbed interfaces with ease.
Rails provides HTML helpers to simplify the use of Turbo Frames through the
[turbo-rails](https://github.com/hotwired/turbo-rails) gem.
Using this gem, you can add a Turbo Frame to your application with the `turbo_frame_tag` helper
like this:
```erb
<%= form_with(model: @article, id: "new-article", local: false) do |form| %>
...
<%= turbo_frame_tag dom_id(post) do %>
<div>
<%= link_to post.title, post_path(path) %>
</div>
<% end %>
```
This will generate the following HTML:
### Turbo Streams
```html
<form id="new-article" action="/articles" accept-charset="UTF-8" method="post" data-remote="true">
...
</form>
[Turbo Streams](https://turbo.hotwired.dev/handbook/streams) deliver page changes as fragments of
HTML wrapped in self-executing `<turbo-stream>` elements. Turbo Streams allow you to broadcast
changes made by other users over WebSockets and update pieces of a page after a form submission
without requiring a full page load.
Rails provides HTML and server-side helpers to simplify the use of Turbo Streams through the
[turbo-rails](https://github.com/hotwired/turbo-rails) gem.
Using this gem, you can render Turbo Streams from a controller action:
```ruby
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.turbo_stream
else
format.html { render :new, status: :unprocessable_entity }
end
end
end
```
Note the `data-remote="true"`. Now, the form will be submitted by Ajax rather
than by the browser's normal submit mechanism.
Rails will automatically look for a `.turbo_stream.erb` view file and render that view when found.
You probably don't want to just sit there with a filled out `<form>`, though.
You probably want to do something upon a successful submission. To do that,
bind to the `ajax:success` event. On failure, use `ajax:error`. Check it out:
Turbo Stream responses can also be rendered inline in the controller action:
```js
window.addEventListener("load", () => {
const element = document.querySelector("#new-article");
element.addEventListener("ajax:success", (event) => {
const [_data, _status, xhr] = event.detail;
element.insertAdjacentHTML("beforeend", xhr.responseText);
});
element.addEventListener("ajax:error", () => {
element.insertAdjacentHTML("beforeend", "<p>ERROR</p>");
});
});
```ruby
def create
@post = Post.new(post_params)
respond_to do |format|
if @post.save
format.turbo_stream { render turbo_stream: turbo_stream.prepend('posts', partial: 'post') }
else
format.html { render :new, status: :unprocessable_entity }
end
end
end
```
Obviously, you'll want to be a bit more sophisticated than that, but it's a
start.
Finally, Turbo Streams can be initiated from a model or a background job using built-in helpers.
These broadcasts can be used to update content via a WebSocket connection to all users, keeping
page content fresh and bringing your application to life.
#### link_to
To broadcast a Turbo Stream from a model combine a model callback like this:
[`link_to`](https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to)
is a helper that assists with generating links. It has a `:remote` option you
can use like this:
```ruby
class Post < ApplicationRecord
after_create_commit { broadcast_append_to('posts') }
end
```
With a WebSocket connection set up on the page that should receive the updates like this:
```erb
<%= link_to "an article", @article, remote: true %>
<%= turbo_stream_from "posts" %>
```
which generates
Replacements for Rails/UJS Functionality
----------------------------------------
```html
<a href="/articles/1" data-remote="true">an article</a>
```
Rails 6 shipped with a tool called UJS that allows developers to override the method of `<a>` tags
to perform non-GET requests after a hyperlink click and to add confirmation dialogs before
executing an action. This was the default before Rails 7, but it is now recommended to use Turbo
instead.
You can bind to the same Ajax events as `form_with`. Here's an example. Let's
assume that we have a list of articles that can be deleted with just one
click. We would generate some HTML like this:
### Method
Clicking links always results in an HTTP GET request. If your application is
[RESTful](https://en.wikipedia.org/wiki/Representational_State_Transfer), some links are in fact
actions that change data on the server, and should be performed with non-GET requests. This
attribute allows marking up such links with an explicit method such as "post", "put", or "delete".
Turbo will scan `<a>` tags in your application for the `turbo-method` data attribute and use the
specified method when present, overriding the default GET action.
For example:
```erb
<%= link_to "Delete article", @article, remote: true, method: :delete %>
```
and write some JavaScript like this:
```js
window.addEventListener("load", () => {
const links = document.querySelectorAll("a[data-remote]");
links.forEach((element) => {
element.addEventListener("ajax:success", () => {
alert("The article was deleted.");
});
});
});
```
#### button_to
[`button_to`](https://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-button_to) is a helper that helps you create buttons. It has a `:remote` option that you can call like this:
```erb
<%= button_to "An article", @article, remote: true %>
```
this generates
```html
<form action="/articles/1" class="button_to" data-remote="true" method="post">
<input type="submit" value="An article" />
</form>
```
Since it's just a `<form>`, all the information on `form_with` also applies.
### Customize Remote Elements
It is possible to customize the behavior of elements with a `data-remote`
attribute without writing a line of JavaScript. You can specify extra `data-`
attributes to accomplish this.
#### `data-method`
Activating hyperlinks always results in an HTTP GET request. However, if your
application is [RESTful](https://en.wikipedia.org/wiki/Representational_State_Transfer),
some links are in fact actions that change data on the server, and must be
performed with non-GET requests. This attribute allows marking up such links
with an explicit method such as "post", "put" or "delete".
The way it works is that, when the link is activated, it constructs a hidden form
in the document with the "action" attribute corresponding to "href" value of the
link, and the method corresponding to `data-method` value, and submits that form.
NOTE: Because submitting forms with HTTP methods other than GET and POST isn't
widely supported across browsers, all other HTTP methods are actually sent over
POST with the intended method indicated in the `_method` parameter. Rails
automatically detects and compensates for this.
#### `data-url` and `data-params`
Certain elements of your page aren't actually referring to any URL, but you may want
them to trigger Ajax calls. Specifying the `data-url` attribute along with
the `data-remote` one will trigger an Ajax call to the given URL. You can also
specify extra parameters through the `data-params` attribute.
This can be useful to trigger an action on check-boxes for instance:
```html
<input type="checkbox" data-remote="true"
data-url="/update" data-params="id=10" data-method="put">
```
#### `data-type`
It is also possible to define the Ajax `dataType` explicitly while performing
requests for `data-remote` elements, by way of the `data-type` attribute.
### Confirmations
You can ask for an extra confirmation of the user by adding a `data-confirm`
attribute on links and forms. The user will be presented with a JavaScript `confirm()`
dialog containing the attribute's text. If the user chooses to cancel, the action
doesn't take place.
Adding this attribute on links will trigger the dialog on click, and adding it
on forms will trigger it on submit. For example:
```erb
<%= link_to "Dangerous zone", dangerous_zone_path,
data: { confirm: 'Are you sure?' } %>
<%= link_to "Delete post", post_path(post), data: { turbo_method: "delete" } %>
```
This generates:
```html
<a href="..." data-confirm="Are you sure?">Dangerous zone</a>
<a data-turbo-method="delete" href="...">Delete post</a>
```
The attribute is also allowed on form submit buttons. This allows you to customize
the warning message depending on the button which was activated. In this case,
you should **not** have `data-confirm` on the form itself.
An alternative to changing the method of a link with `data-turbo-method` is to use Rails
`button_to` helper. For accessibility reasons, actual buttons and forms are preferable for any
non-GET action.
### Confirmations
### Automatic disabling
You can ask for an extra confirmation of the user by adding a `data-turbo-confirm` attribute on
links and forms. The user will be presented with a JavaScript `confirm()` dialog containing the
attributes text. If the user chooses to cancel, the action doesn't take place.
It is also possible to automatically disable an input while the form is submitting
by using the `data-disable-with` attribute. This is to prevent accidental
double-clicks from the user, which could result in duplicate HTTP requests that
the backend may not detect as such. The value of the attribute is the text that will
become the new value of the button in its disabled state.
This also works for links with `data-method` attribute.
For example:
Adding this attribute on links will trigger the dialog on click, and adding it on forms will
trigger it on submit. For example:
```erb
<%= form_with(model: Article.new) do |form| %>
<%= form.submit data: { disable_with: "Saving..." } %>
<% end %>
<%= link_to "Delete post", post_path(post), data: { turbo_method: "delete", turbo_confirm: "Are you sure?" } %>
```
This generates a form with:
This generates:
```html
<input data-disable-with="Saving..." type="submit">
<a href="..." data-confirm="Are you sure?" data-turbo-method="delete">Delete post</a>
```
### Rails-ujs event handlers
Rails 5.1 introduced rails-ujs and dropped jQuery as a dependency.
As a result the Unobtrusive JavaScript (UJS) driver has been rewritten to operate without jQuery.
These introductions cause small changes to `custom events` fired during the request:
NOTE: Signature of calls to UJS's event handlers has changed.
Unlike the version with jQuery, all custom events return only one parameter: `event`.
In this parameter, there is an additional attribute `detail` which contains an array of extra parameters.
For information about the previously used `jquery-ujs` in Rails 5 and earlier, read the [`jquery-ujs` wiki](https://github.com/rails/jquery-ujs/wiki/ajax).
| Event name | Extra parameters (event.detail) | Fired |
|---------------------|---------------------------------|-------------------------------------------------------------|
| `ajax:before` | | Before the whole ajax business. |
| `ajax:beforeSend` | [xhr, options] | Before the request is sent. |
| `ajax:send` | [xhr] | When the request is sent. |
| `ajax:stopped` | | When the request is stopped. |
| `ajax:success` | [response, status, xhr] | After completion, if the response was a success. |
| `ajax:error` | [response, status, xhr] | After completion, if the response was an error. |
| `ajax:complete` | [xhr, status] | After the request has been completed, no matter the outcome.|
Example usage:
```js
document.body.addEventListener("ajax:success", (event) => {
const [data, status, xhr] = event.detail;
});
```
### Stoppable events
You can stop execution of the Ajax request by running `event.preventDefault()`
from the handlers methods `ajax:before` or `ajax:beforeSend`.
The `ajax:before` event can manipulate form data before serialization and the
`ajax:beforeSend` event is useful for adding custom request headers.
If you stop the `ajax:aborted:file` event, the default behavior of allowing the
browser to submit the form via normal means (i.e. non-Ajax submission) will be
canceled, and the form will not be submitted at all. This is useful for
implementing your own Ajax file upload workaround.
Note, you should use `return false` to prevent an event for `jquery-ujs` and
`event.preventDefault()` for `rails-ujs`.
Server-Side Concerns
--------------------
Ajax isn't just client-side, you also need to do some work on the server
side to support it. Often, people like their Ajax requests to return JSON
rather than HTML. Let's discuss what it takes to make that happen.
### A Simple Example
Imagine you have a series of users that you would like to display and provide a
form on that same page to create a new user. The index action of your
controller looks like this:
```ruby
class UsersController < ApplicationController
def index
@users = User.all
@user = User.new
end
# ...
```
The index view (`app/views/users/index.html.erb`) contains:
```erb
<b>Users</b>
<ul id="users">
<%= render @users %>
</ul>
<br>
<%= form_with model: @user, local: false do |form| %>
<%= form.label :name %><br>
<%= form.text_field :name %>
<%= form.submit %>
<% end %>
```
The `app/views/users/_user.html.erb` partial contains the following:
```erb
<li><%= user.name %></li>
```
The top portion of the index page displays the users. The bottom portion
provides a form to create a new user.
The bottom form will call the `create` action on the `UsersController`. Because
the form's remote option is set to true, the request will be posted to the
`UsersController` as an Ajax request, looking for JavaScript. In order to
serve that request, the `create` action of your controller would look like
this:
```ruby
# app/controllers/users_controller.rb
# ......
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
format.html { redirect_to @user, notice: 'User was successfully created.' }
format.js
format.json { render json: @user, status: :created, location: @user }
else
format.html { render action: "new" }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
```
Notice the `format.js` in the `respond_to` block: that allows the controller to
respond to your Ajax request. You then have a corresponding
`app/views/users/create.js.erb` view file that generates the actual JavaScript
code that will be sent and executed on the client side.
```js
var users = document.querySelector("#users");
users.insertAdjacentHTML("beforeend", "<%= j render(@user) %>");
```
NOTE: JavaScript view rendering doesn't do any preprocessing, so you shouldn't use ES6 syntax here.
Turbolinks
----------
Rails ships with the [Turbolinks library](https://github.com/turbolinks/turbolinks),
which uses Ajax to speed up page rendering in most applications.
### How Turbolinks Works
Turbolinks attaches a click handler to all `<a>` tags on the page. If your browser
supports
[PushState](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history#The_pushState%28%29_method),
Turbolinks will make an Ajax request for the page, parse the response, and
replace the entire `<body>` of the page with the `<body>` of the response. It
will then use PushState to change the URL to the correct one, preserving
refresh semantics and giving you pretty URLs.
If you want to disable Turbolinks for certain links, add a `data-turbolinks="false"`
attribute to the tag:
```html
<a href="..." data-turbolinks="false">No turbolinks here</a>.
```
### Page Change Events
You'll often want to do some sort of processing upon
page load. Using the DOM, you'd write something like this:
```js
window.addEventListener("load", () => {
alert("page has loaded!");
});
```
However, because Turbolinks overrides the normal page loading process, the
event that this relies upon will not be fired. If you have code that looks like
this, you must change your code to do this instead:
```js
document.addEventListener("turbolinks:load", () => {
alert("page has loaded!");
});
```
For more details, including other events you can bind to, check out [the
Turbolinks
README](https://github.com/turbolinks/turbolinks/blob/master/README.md).
Cross-Site Request Forgery (CSRF) token in Ajax
----
When using another library to make Ajax calls, it is necessary to add
the security token as a default header for Ajax calls in your library. To get
the token:
```js
const token = document.getElementsByName(
"csrf-token"
)[0].content;
```
You can then submit this token as a `X-CSRF-Token` header for your
Ajax request. You do not need to add a CSRF token for GET requests,
only non-GET ones.
You can read more about Cross-Site Request Forgery in the [Security guide](https://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf).
Other Resources
---------------
Here are some helpful links to help you learn even more:
* [rails-ujs wiki](https://github.com/rails/rails/tree/main/actionview/app/assets/javascripts)
* [Railscasts: Unobtrusive JavaScript](http://railscasts.com/episodes/205-unobtrusive-javascript)
* [Railscasts: Turbolinks](http://railscasts.com/episodes/390-turbolinks)