You need to wrap your JS inside of DOM ready event:
$(document).ready(function() { //your code goes here });
In case of Rails with Turbolinks:
.coffee
ready = -> //your coffeescript goes here $(document).ready(ready) $(document).on('page:load', ready)
.js
var ready = function() { //your javascript goes here }; $(document).ready(ready); $(document).on('page:load', ready);
Your assets/javascript/test.js file:
var ready = function() { console.log(document.body); }; $(document).ready(ready); $(document).on('page:load', ready);
Explanation:
From Using jQuery Core :
A page can't be manipulated safely until the document is "ready." jQuery detects this state of readiness for you. Code included inside $( document ).ready() will only run once the page Document Object Model (DOM) is ready for JavaScript code to execute. Code included inside $( window ).load(function() { ... }) will run once the entire page (images or iframes), not just the DOM, is ready.
JS is executed as soon as it is encountered in your document. Rails usually imports your JS via application.js file which loaded in the head of your document. At this point the rest of your document hasn't been even loaded so JS executes and finds partial unfinished DOM - body hasn't been rendered yet.
To postpone execution of your JS you can wrap it - aka register it - with $(document).ready event. You can have multiple calls to $(document).ready across multiple files. Usually it is a good idea to wrap your files with it.
When entire document is loaded, all you DOM elements has been loaded, jQuery will fire ready event and all your code registered with $(document).ready will get executed.
Turbolinks
From Working with JavaScript in Rails # Turbolinks:
Turbolinks attaches a click handler to all <a>
on the page. If your browser supports PushState, Turbolinks will make an Ajax request for the page, parse the response, and replace the entire of the page with the of the response. It will then use PushState to change the URL to the correct one, preserving refresh semantics and giving you pretty URLs.
When writing CoffeeScript, you'll often want to do some sort of processing upon page load. With jQuery, you'd write something like this:
$(document).ready -> alert "page has loaded!"
However, because Turbolinks overrides the normal page loading process, the event that this relies on will not be fired. If you have code that looks like this, you must change your code to do this instead:
$(document).on "page:change", -> alert "page has loaded!"
Resources: