The _data/needs.yml file is the heart of this recipe. While this might have been included as an element of the _config.yml file, I decided to create a separate file because the needs get pretty wordy and felt like clutter in the config file.
The file contains a series of entries, each one beginning with an identifier (a name) you supply for a given need. These are the same identifiers that you will use when defining relationships with other needs or declaring the need on a given page’s YML frontmatter.
Under that identfier you can have a number of elements, all of which are optional:
needsis an array of other needs upon which this need is dependent;javascriptis either the URL of a script on the web somewhere or the filename of a script in your local Jekyll implmentation;idis an identifier which should be associated with the<script>tag that loads this script;asyncis a boolean which you can set totrueif you want the<script>tag to specify ansyncronous loading of the javascript.
For example, if I want to use Disqus to add the option for commenting to some of my pages, but I also want to hide the Disqus elements of the page until the user clicks on a comment button, I might include the following in my needs.yml file:
comments:
javascript: /js/disqus.js
needs:
- jquery
- disqus_embed
- disqus_count
disqus_embed:
javascript: "//shortname.disqus.com/embed.js"
async: true
disqus_count:
javascript: "//shortname.disqus.com/count.js"
id: dsq-count-scr
async: true
This indictes that if I declare that a page needs comments, then I need to load jQuery and a couple scripts from Disqus before I load my own /js/disqus.js script to handle the revealing of the Disqus elements of the page. Althought it has no bearing on the recipe for needs, here is my /js/disqus.js script for completness:
jQuery(document).ready(function($) {
$("#comment-button").click(function() {
$("#comment-button").slideUp(400, function() {
$("#disqus_thread").slideDown(800);
});
});
});
On pages and posts
To include the need on a page or post (or in any other collection item you may have), you just need to add a reference to it in the YML frontmatter. For example, if I want to include the Disqus comments defined above I would simply add this code to the YML frontmatter of a page:
needs:
- comments
In layouts
In order for the needs to be rendered on the page, the Jekyll layout for a given page must have a bit of Liquid added. In my case I include this code just before the closing </body> tag on a defaults.html layout that is the basis for all my other layouts.
{% include walk_needs.html needs = page.needs %}
That’s all that is required to get the JavaScript loaded, but in the case of Disqus there is also a bit of HTML and page-specifc JavaScript that must be included. To manage this I have elsewhere on the layout included:
{% if page.needs contains "comments" %}
<script>
var disqus_config = function () {
this.page.url = "{{ site.url }}{{ page.url }}";
this.page.identifier = "{{ site.url }}{{ page.url }}";
this.page.title = "{{ page.title | replace: '"', '\"' }}";
};
</script>
<div id="comment-button" class="disqus-comment-count button" data-disqus-identifier="{{ site.url }}{{ page.url }}">Be the first to comment</div>
<div id="disqus_thread"></div>
{% endif %}
The includes
While you don’t have to change these files, you will need to know what is in them!
_include/walk_needs.html:
{% capture list %}{% include walk_needs_recursion.html needs=include.needs %}{% endcapture %}
{% assign needs = list | strip_newlines | replace: ' ', '' | split: ',' | uniq %}
{% for need in needs %}
{% assign printing = site.data.needs[need] %}
{% assign javascript = printing.javascript %}
{% assign id = printing.id %}
{% if javascript %}
<script {% if id %}id="{{ id }}"{% endif %} src="{{ javascript | reurl }}" {% if printing.async %}async{% endif %}></script>
{% endif %}
{% endfor %}
_include/walk_needs_recursion.html:
{% for need in include.needs %}
{% assign testing_need = site.data.needs[need] %}
{% assign dependencies = testing_need.needs %}
{% if dependencies %}
{% include walk_needs_recursion.html needs = dependencies %}
{% endif %}
{{ need }},
{% endfor %}
Bottom line
So all this boils down to the fact that I can include various features only on pages where I need them by including some needs YML in their frontmatter. I don’t have to worry about dependencies or special inclusions at that point. I also have a simple mechanism for documenting the JavaScript used on my site and its dependencies on other JavaScript tools or libraries in the _data/needs.yml file.
This has saved a lot of headaches and I like the result even more than the WordPress wp_enqueue_script that inspired it. I’d love comments if you see things I could improve.
In the future I plan to enhance the _include/walk_needs.html code so that it can accomodate CSS and JavaScript to rendered in the header as well as the current JavaScript at the end of the page. I plan to do that by introducing a position prameter to the call and a css element to the _data/needs.yml. Suggestions welcome.