Refills accordian component not working with Middleman?

I’m working on a Middleman site and trying to add the accordion component provided by Refills. For reasons which remain unclear to me, the browser does not seem to read in the javascript properly and thus the accordion doesn’t respond when clicked.

When I view ‘all.js’ in Chrome dev tools, I can see that the relevant script is being included by sprockets. Then, if I copy the js into the console, the accordion suddenly starts working. I’ve experimented with various things (such as including the accordion js at different points in the manifest) but so far no luck.

To demonstrate the issue, I’ve cloned the Middleman sample app created by @christoomey and added the accordion to it. Same issue there. When you load the page in the browser, the accordion appears but does not respond to clicks. The JS shows up in dev tools. If you paste it in to the JS console, everything works until the next page load.

Any thoughts? Thank you!

UPDATE: As I continued troubleshooting this issue, it occured to me to try wrapping the Refills accordion snippet in a “document.ready” function, which, if I understand correctly, ensures that the code is loaded when the DOM is loaded. (Sorry, my javascript skills are a bit sad.) Anyway, when I made this change, the accordion began working as expected. Yay!

Still, I’d appreciate any comments/suggestions on what I missed here:

  • Is my fix the best way to solve this issue?
  • If so, should the code snippet on Refills be updated? (Other Refills components do call document.ready, such as the navigation bars)
  • Is it correct/incorrect to assume that snippets from Refills can be dropped into the asset pipeline as-is and work as expected? This was has been my understanding heretofore, but perhaps I am mistaken?

Again, thanks for any input!

Hi @joshukraine, $(document).ready is indeed the needed fix. To give a little more detail, your code preforms executes a jQuery selector, $('.js-accordion-trigger'), to find the element with class “.js-accordion-trigger”. If run too early, this query will not find an element and will return an empty jQuery object. This won’t raise any sort of error as jQuery is designed to silently ignore operations performed on an empty selector set.

By wrapping in the document ready call, you ensure that the element will be available and that your function will be properly bound.

As a general rule, you always want to wrap any code that uses jQuery selectors to bind events to elements in a document ready call. I don’t know that we want to update the refills examples as typically you would have one outer document ready call, and then multiple event bindings within (see below), but we could likely add a general note.

$(document).ready(function() {
  // bind click events to this thing

  // bind hover event to this other thing

  // lastly, bind an event to window resize
});

Hope this all helps, but let me know if you have any other questions.

– Chris

Hey Chris,

Thanks for the response. As for the JS explanation, I think I understand. That brings up more of a workflow question though. Let’s say I’m building a Rails app, and I want to incorporate multiple Refills components. For example, a nav bar, followed by some tabs, and later the accordion unit. The way I have done this in the past is to create a separate js file for each component and include each in the all.js manifest. (I would do the same for the css) Both the nav and the tabs components have $(document).ready calls in the provided snippets whereas accordion does not. So is OK to simply be stacking all these up in the js manifest during the course of an app’s development or is it better to somehow ensure that there is only a single call to $(‘document’).ready?

Hi @joshukraine, it’s fine to have multiple $(document).ready calls, one in each of the files, if that is your preference. The key is that all event binding and DOM manipulation code that is intended to run when the page loads is wrapped in a $(document).ready.