In Rails you really want to forget the concept of "I just want to load this JS file on this page" - I would consider it an anti-pattern that will hold you back.
Rails uses both an assets pipeline / webpacker to streamline your JS delivery, turbolinks to "ajaxify" regular page loads in older versions and Rails UJS does a lot of ajax trickery. Which means that you very likely will be dealing with ajax and persistent browser sessions which make the idea into a real problem as your JS leaks into contexts you didn't intially imagine or just plain won't work unless you're reloading the whole page.
Instead think in terms of event based programming and behaviors that you can augment UI elements with. Think "I want this kind of button to do X" instead of "I want this specific thing on this page to do X". Use event handlers and css-classes to reach that goal:
document.addEventListener("click", (event) => {
if (!event.target.matches('.magic-button')) return;
console.log('You clicked a magical button!');
});
This makes your JS decoupled from the page itself and reusable and actually makes your intial problem moot. If you don't want to behavior on other pages then don't add the magic-button class.
If you REALLY want to do "per page" javascript one approach that I have used it to attach the controller_name and action_name to the body element:
<body data-controller="<%= controller_name" %>" data-action="<%= action_name %>">
That lets you know where you are in your application simply by reading the data attributes:
// given controller = foos and action = bar the events are:
// * MyApp:loaded
// * MyApp:foos#bar
// * MyApp:foos
// * MyApp:#bar
const firePageEvents = (event) => {
// read the data attributes off the body element
const body = document.querySelector('body');
const details = {
action: body.dataset.action,
controller: body.dataset.controller
};
const eventNames = [
"MyApp:loaded",
`MyApp:${controller}#${action}`,
`MyApp:${controller}`,
`MyApp:#${action}`
];
// fire the events
eventNames.forEach((name) => {
let event = new CustomEvent(name, { detail: details });
document.dispatch(event);
});
}
// fires the events either when turbolinks replaces the page
// or when the DOM is ready
if (window.Turbolinks && Turbolinks.supported) {
document.addEventListener("turbolinks:load", (event) => {
firePageEvents(event);
});
} else {
document.addEventListener("DOMContentLoaded", (event) => {
firePageEvents(event);
});
}
// this will be trigged when any page is loaded
document.addEventListener("MyApp:loaded", (event) => {
switch(event.details.action) {
case 'about':
console.log('about page loaded');
break;
case 'friends':
console.log('friends page loaded');
break;
}
});
// this will be trigged when the an action named "about" is rendered:
document.addEventListener("MyApp:#about", (event) => {
console.log('About page loaded', event.details);
});
// this will be triggered on the users load page
document.addEventListener("MyApp:users#index", (event) => {
console.log('User#index page loaded', event.details);
});
We then fire custom events that you can attach event listeners to make JS fire for specific controllers / actions.
This code belongs in your assets pipeline / packs where its effectivly concatenated and delivered.