Best Way to Add Page Specific JavaScript in a Rails 3 App

Best way to add page specific JavaScript in a Rails 3 app?

What I like to do is include the per-view Javascript in a content_for :head block and then yield to that block in your application layout. For example

If it's pretty short then:

<% content_for :head do %>
<script type="text/javascript">
$(function() {
$('user_rating_positve').click(function() {
$('some_div').show();
}
});
</script>
<% end %>

or, if longer, then:

<% content_for :head do %>
<script type="text/javascript">
<%= render :partial => "my_view_javascript"
</script>
<% end %>

Then, in your layout file

<head>
...
<%= yield :head %>
</head>

Using Rails 3.1, where do you put your page specific JavaScript code?

I appreciate all the answers... and I don't think they are really getting at the problem. Some of them are about styling and don't seem to relate... and others just mention javascript_include_tag... which I know exists (obviously...) but it would appear that the Rails 3.1 way going forward is to wrap up all of your Javascript into 1 file rather than loading individual Javascript at the bottom of each page.

The best solution I can come up with is to wrap certain features in div tags with ids or classes. In the javascript code. Then you just check if the id or class is on the page, and if it is, you run the javascript code that is associated with it. This way if the dynamic element is not on the page, the javascript code doesn't run - even though it's been included in the massive application.js file packaged by Sprockets.

My above solution has the benefit that if a search box is included on 8 of the 100 pages, it will run on only those 8 pages. You also won't have to include the same code on 8 of the pages on the site. In fact, you'll never have to include manual script tags on your site anywhere ever again - except to maybe preload data.

I think this is the actual answer to my question.

Using Rails 6, where do you put your “page specific” JavaScript code?

I'll describe a few options in order of increasing level of difficulty with regards to experience with Webpack and Webpacker.

  1. Forget page-specific JavaScript and put everything in the application.js bundle. This will most definitely be the easiest approach to understand. For a small application, the tradeoff may well be worth it as having an application that's easier to maintain may outweigh added cost of learning how to best to split up your JS code with Webpack with little performance gain.

  2. Use dynamic imports to lazy load your page-specific code. In this scenario, you would still place all your JavaScript within the application.js dependency graph, but not all of this code would be loaded up-front. Webpack recognizes the dynamic import() function when it compiles your JS and will split the imported chunk out into a separate file that can be loaded on-demand in the browser using a JS framework router or a simple trigger.

    For example, you could have code that looks like this:

    if (document.querySelectorAll(".post-index").length) {
    import("./post/index") // webpack will load this JS async
    }
  3. Use page-specific "packs" combined with the splitChunks configuration API. In this scenario, instead of using an application.js pack, you would construct one for each page you want to treat differently, e.g, posts.js, admin.js etc. Using the splitChunks plugin means that your bundles can properly share code. I highly recommend treading carefully with this approach until you understand how Webpack works OR be willing to go through the process of learning Webpack in choosing this path. Webpack typically works best on the assumption you use only one entry point per page, otherwise, you may end up duplicate code across bundles unless you know what you're doing.

Rails - how to include Javascript files only on certain pages

You can put any Javascript files that you want to load manually in the public/javascripts/lib directory of your application, and they will not be included in the assets pipeline. You can then load them manually on the pages that need them.

For instance, in one project, I use the Chosen jQuery plugin, and I load it like so:

<script type="text/javascript" src="/javascripts/lib/chosen.jquery.min.js"></script>

Rails will source the public files from public/, so you only need to reference your files from there (remove the public/ bit).

This project is fairly large, with 88 controllers, 662 actions, and a total of 38 custom javascript libraries that get used sporadically around the app, including markdown editors, charting libraries, and even jQuery UI.

To manage the sprawl and to keep each page as tight as possible, I have done 2 things: 1) in my controller, I set an instance variable,@page_libs, to list the libs to load, and 2) the layout uses the values in @page_libs to include the specialty Javascript when required.

A controller action might look like this:

def edit
@products = products.find(params[:id])
@page_libs = [:ui, :textile]
end

And the app/views/layouts/application.html.erb includes this in the correct place:

<%- if @page_libs&.include?(:ui) || @page_libs&.include?(:table) %>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.min.js"></script>
<script type="text/javascript" src="/javascripts/lib/chosen.jquery.min.js"></script>
<% end -%>
<%- if @page_libs&.include?(:swiper) %>
<script type="text/javascript" src="/javascripts/lib/idangerous.swiper.min.js"></script>
<% end -%>
<%- if @page_libs&.include?(:table) %>
<script type="text/javascript" src="/javascripts/lib/jquery.handsontable.full.js"></script>
<% end -%>
<%- if @page_libs&.include?(:textile) %>
<script type="text/javascript" src="/javascripts/lib/textile.js" charset="utf-8"></script>
<% end -%>

Note that the first include is for jQuery UI, which I load from a CDN, rather than from my app's public. This technique works just as well with external libraries, as well as those that you host. In fact, most pages in my app only depend on 2 external libraries (jQuery and Underscore.js), but have the option of loading up to 16 other Javascript libraries from external sources. Limiting external libraries on the page can significantly reduce your page load times, which is a direct performance boost for your application.

Sometimes, a Javascript library will include CSS components, as well. Or, you may even have page-specific CSS to include. The same approach can be taken with external stylesheet. These are the corresponding page-specific stylesheet "includes" for the above Javascript libraries:

<%- if @page_libs&.include?(:ui) %>
<link rel="stylesheet" type="text/css" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/smoothness/jquery-ui.css">
<link rel="stylesheet" type="text/css" href="/stylesheets/lib/chosen.min.css">
<% end -%>
<%- if @page_libs&.include?(:swiper) %>
<link rel="stylesheet" type="text/css" href="/stylesheets/lib/idangerous.swiper.css">
<% end -%>

This way, I have a single point in the project to manage the libraries, regardless of how many (or few) are required for a page. I'll probably eventually create a series of custom before_action handlers in the ApplicationController to define which the libraries a page needs included. Something like this:

before_action: :include_library_ui,     only: [:new, :edit]
before_action: :include_library_swiper, only: [:show]

This would clean up the controller actions a bit more and make it easier to identify dependencies. However, given the size of my codebase and the pressing tasks remaining, I haven't taken this leap yet. Maybe it will inspire you to do so to start out.

Where do I place my javascript for a specific page in a Rails 5.2 application?

Your scripts should ideally be prepackaged and concatenated into a single file to avoid multiple HTTP requests. Instead of relying on loading a particular set of JavaScript on a specific page you can just add a data attribute or class to the body element and use it to toggle your behaviors. This is not only better for performance but will avoid the issues tied to turbolinks and having a persistent browser session across page visits.

I wrote a gem for this a while back that has not been maintained in quite some time but I solved it by basically doing:

<body data-action="<%= action_name %>" data-controller="<%= controller_name %>">

$(document).on('turbolinks:load', function(){
let data = $('body').data();
// pagescript: is just the namespace for the events to avoid potential name collisions
// Use whatever you want instead
$(document).trigger('pagescript:' + data.controller + '#' + data.action, data)
.trigger('pagescript:' + data.controller + '#*', data)
.trigger('pagescript:' + '*#' + data.action , data);
});

This triggers a set of custom events on every page change. You can add event listeners to listen for specific pages or actions:

// A specific controller_name and action
$(document).on('pagescript:controller_name#action_name', (e) => { });
// Any action belonging to a specific controller
$(document).on('pagescript:controller_name#*', (e) => { });
// Any action matching action_name
$(document).on('pagescript:*#action_name', (e) => { });

For example:

$(document).on('pagescript:users#new', (e) => { 
alert("Welcome new user!");
});

But the problem can also be solved by better code design. Think in terms of modules that can be used to enhance the behavior in any page instead of writing code that enhances that specific doodad on the specific page.

Proper Rails way to perform javascript on specific pages

This is a pretty nice turbolinks specific solution:

<body data-controller="<%= controller.controller_name %>" data-action="<%= controller.action_name %>">

// Instead of listening for document ready$(document).on('users#show:loaded', function(){    $('#logger').append('<li>Users Show action loaded</li>');});
// Instead of listening for document ready$(document).on('users:loaded', function(){ $('#logger').append('<li>Some users controller method loaded</li>');});
// Turbolinks fires "page:change" when it changes the page content.// you could change this to just document ready if you are not using // turbolinks.$(document).on("page:change", function(){ var data = $('body').data(); $(document).trigger(data.controller + ':loaded'); $(document).trigger(data.controller + '#' + data.action + ':loaded');});
// This is just for demonstration purposes...$(document).trigger("page:change");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><body data-controller="users" data-action="show">
<ul id="logger"></ul></body>

Rails js files & Page specific js

Before body close tag and just after <%= javascript_include_tag 'application' %>

add <%= yield :page_scripts %>

Then anywhere (usually on top) in a specific view set your scripts with:

<% content_for :page_scripts do %>
<script>alert( "My nice scripts..." );</script>
<% end %>


Related Topics



Leave a reply



Submit