Skip to Main Content
Job board hero

Creating a Simple Job Board in Craft CMS

In this post I will show you how to create a simple job board for a medium to large sized company. Jobs are available internally for 5 days first and then move to public listing. Additionally the jobs will have keyword filtering that updates the listing as you type.

This site that this job board was originally built for had branches located across Canada. Requirements included listing all positions across Canada, linking to the branch the job is located at, and the ability to filter all job listings in order to make it easier to find a job that is right for the visitor.

The job board that I built is a simple listing in a table with columns for position, location, employment type, and Brand/Division. The position links off to an entry page and the location links off to the branch that the job opening is located at.

Above the list is an input field where you can filter the listing by typing into it.

Section and Field Settings

Each job opportunity has fields for:

  • Title
  • Branch (entries field)
  • Location Title (input for unique branch name on this page)
  • Brand/Division (input field)
  • Employment Type (input field)
  • Copy (redactor)

Set up the careers section and point the entries to the careers/_entry template.

The next step is to set up a route so that we can have internal job entries viewable on at a url that is only available if you know the link. Go to the routes setting and add a route like this internal/careers/slug and have it point to the careers/_entry template as well.

Public Listing Template

This is the code for the public careers listing template where job opportunities that are more than 5 days old will display.

    
      <h3>Employment Opportunities</h3>

          <input type="search" class="light-table-filter form-control input-md" data-table="order-table" placeholder="{{ "careers_filter"|t}}">

          <table class="table table-striped table-condensed order-table">
            <thead>
              <tr>
                <th>Position</th>
                <th>Location</th>
                <th>Employment Type</th>
                <th>Brand/Division</th>
              </tr>
            </thead>

            <tbody>

              {# show only entries more than 5 days old #}
              {% set dateRestriction = now|date_modify('5:00 -5 days') %}
              {% set postDateParam = '< ' ~ dateRestriction|date('U') %}

              {% for entry in craft.entries.section('careers').orderBy('title').postDate(postDateParam).all() %}
                <tr>
                  <td><strong><a href="{{ entry.url }}">{{ entry.title }}</a></strong></td>

                  {% set branch = entry.branch.one() ?? null %}

                  <td>
                  {% if branch|length %}<a href="{{ branch.url }}" class="branch-link">{{ entry.location }}</a>
                  {% else %}
                    {{ entry.location }}
                  {% endif %}
                  </td>
                  <td>{{ entry.division }}</td>
                  <td>{{ entry.brandDivision }}</td>

                </tr>
              {% endfor %}

            </tbody>

          </table>
    
  

Filter the Entries

Now that we have all the entries displaying it's time to enable filtering. I cribbed the javascript from this Code Pen. Add this to your template. Since it's only needed on this template and the private job board I've added it to a {% block pageJavascript %} {% endblock %} block which gets rendered via the layout template.

    
      <script>
    (function (document) {
      'use strict';

      var LightTableFilter = (function (Arr) {

        var _input;

        function _onInputEvent(e) {
          _input = e.target;
          var tables = document.getElementsByClassName(_input.getAttribute('data-table'));
          Arr.forEach.call(tables, function (table) {
            Arr.forEach.call(table.tBodies, function (tbody) {
              Arr.forEach.call(tbody.rows, _filter);
            });
          });
        }

        function _filter(row) {
          var text = row.textContent.toLowerCase(), val = _input.value.toLowerCase();
          row.style.display = text.indexOf(val) === -1 ? 'none' : 'table-row';
        }

        return {
          init: function () {
            var inputs = document.getElementsByClassName('light-table-filter');
            Arr.forEach.call(inputs, function (input) {
              input.oninput = _onInputEvent;
            });
          }
        };
      })(Array.prototype);

      document.addEventListener('readystatechange', function () {
        if (document.readyState === 'complete') {
          LightTableFilter.init();
        }
      });

    })(document);  
  </script>
    
  

Internal Job Listing

One client requirement was to have job listings available internally for 5 days before they are then available for anyone to apply.

To do this set up a save the above template as a new template at careers/_internal. At this point it works exactly like the other template. To list only entries that are less than 5 days old change this line {% set postDateParam = '< ' ~ dateRestriction|date('U') %} to this: {% set postDateParam = '> ' ~ dateRestriction|date('U') %} notice the angle bracket has changed direction.

Now the internal page only lists entries that are new and not available to the public.

Important: be sure to disable search engines from indexing the internal template and also remove the urls from any sitemaps.