Splunk input highlighting imageContinuing the thread from our previous article about tooltips, let's discuss more ways to improve the UX of Splunk dashboards, namely highlighting required inputs.

Over the years we’ve seen websites and various user interfaces that describe ways to tell a user that their input is needed. To a developer this can seem unnecessary, but I have participated in enough support calls to know that users need help.

Unfortunately, Splunk dashboards are often simple and unflashy products made by people – myself included – without the knowledge or ability to apply the best UI design principles.

To help address the need, I have come up with another basic pattern to improve Splunk dashboards. The objective is to signal a user when their input is needed in order to drive some charts/output in a Splunk dashboard. Also, adding a layer of filtering, cleansing, or validation to inputs may be desirable, e.g., removing unnecessary whitespace or catching unsafe entries.

As discussed in our previous posts, it all centers around extending the SimpleXML Splunk dashboards with custom JavaScript.

The focus of this post is adding the basic functionality for input highlighting. If an input is blank, the attributes of that element are changed to offer contrast, drawing attention to it. Once user input is applied, the visual attributes return to their default state.

Here are examples of JavaScript functions that apply the changes (see effects in image atop this article). The first function setInputFocus applies background highlighting or a border to the input element, and the subsequent function clearInputFocus undoes the changes. In both cases, the code determines which changes are applied based on the type of Splunk input element that is passed into the function; for example, text boxes and multi-select inputs have background highlighting applied whereas radio buttons get a border.

      // set the highlighting effects on the based element
      function setInputFocus(el) {
        if (el.hasClass('input-text') || el.hasClass('splunk-textinput')) {
          el.find('input[type="text"]')
            .css("border-color", "red")
            .css("box-shadow", "0px 1px 1px rgba(0, 0, 0, 0.075) inset, 0px 0px 8px rgba(222, 79, 79, 0.6");
        } else if (el.hasClass('input-dropdown')) {
          el.find('.select2-choice')
            .css("border-color", "red")
            .css("box-shadow", "0px 1px 1px rgba(0, 0, 0, 0.075) inset, 0px 0px 8px rgba(222, 79, 79, 0.6");
        } else if (el.hasClass('input-multiselect')) {
          el.find('.select2-choices')
            .css("border-color", "red")
            .css("box-shadow", "0px 1px 1px rgba(0, 0, 0, 0.075) inset, 0px 0px 8px rgba(222, 79, 79, 0.6");
        } else {
          el.css("border-style", "double");
        }
      };

      // clear the highlighting effects on the based element
      function clearInputFocus(el) {
        if (el.hasClass('input-text') || el.hasClass('splunk-textinput')) {
          el.find('input[type="text"]')
            .css("border-color", "")
            .css("box-shadow", "");
        } else if (el.hasClass('input-dropdown')) {
          el.find('.select2-choice')
            .css("border-color", "")
            .css("box-shadow", "");
        } else if (el.hasClass('input-multiselect')) {
          el.find('.select2-choices')
            .css("border-color", "")
            .css("box-shadow", "");
        } else {
          el.css("border-style", "none");
        }
      };


With those functions defined, let's consider how to use them. Simply put, if there is no valid value defined for a critical input, it should be highlighted; otherwise it should be cleared to its default state. It is crucial to note the input values and apply the changes accordingly – by inserting a token listener in the JavaScript extensions. What follows is a stripped down example from our Layer8Insight App for Splunk. In this example there are two inputs at the top of the dashboard (see image atop this article), a text box for user-provided search values and a multi-select input for filtering included executables. Their HTML IDs are "search_value" and "target_resources", respectively. Here is the corresponding XML for the top of the dashboard.

    <form onunloadCancelJobs="true" script="input-highliting-example.js">
      ...
      <fieldset autoRun="true" submitButton="true">
        ...
        <input id="search_value" searchWhenChanged="false" token="search_value" type="text">
          <label>Search Value</label>
        </input>
        <input id="target_resources" searchWhenChanged="false" token="target_resources" type="multiselect">
          <label>Included Executables</label>
          <search id="populating_resource_search">
            ...
        </input>
      </fieldset>
      ...
    </form>


With the XML elements defined, the following JavaScript file adds in the logic for highlighting the inputs based on the user-entered data.

For the text box "search_value", the code first ensures that user input is cleaned of all whitespace at the beginning and end of the entered value. If whitespace alone is entered, it completely clears out the input. Once the input is cleaned, the code calls the function checkEmptyTokenFocusForDashboard to start the process of checking if the input is empty and should be highlighted.

For the multi-select input, the function checkEmptyTokenFocusForDashboard is called without cleansing the individual input array elements – it merely checks for entries. Note: search event listeners are set for both the start and completion events of the search (labeled "populating_resource_search") that populates the list of options for the multi-select input "target_resources". This is necessary because Splunk will reset the applied highlighting effects on the multi-select input when the search is updated, e.g., the search timepicker changes.

    // 
    // input-highligting-example.js
    //
    require([
        'underscore',
        'jquery',
        "splunkjs/mvc",
        "splunkjs/ready!",
        "splunkjs/mvc/simplexml/ready!",
    ], function(_, $, mvc) {

      // get token model entities
      var submittedTokens = mvc.Components.get('submitted');
      var defaultTokens = mvc.Components.get('default');

      // Sets token "name" to "value" in defaultTokens.
      // Submit tokens if "submit" argument is true
      function setToken(name, value, submit) {
        if (defaultTokens && typeof name !== 'undefined') {
          defaultTokens.set(name, value);
        }
        if (!!submit) {
          submitTokens();
        }
      }

      // Copy defaultTokems values into submittedTokens
      function submitTokens() {
        if (submittedTokens && defaultTokens) {
          submittedTokens.set(defaultTokens.toJSON());
        }
      }

      // loop through input elements and evaluate each one for focus
      function checkEmptyTokenFocusForDashboard(inputs) {
        if (typeof inputs === 'undefined') {
          return;
        }

        var len = inputs.length;
        var currentValue = undefined;

        for (var i = 0; i < len; i++) {
          currentValue = (defaultTokens.attributes.hasOwnProperty('form.' + inputs[i]) ? 
            defaultTokens.get('form.' + inputs[i]) :
            defaultTokens.get(inputs[i]));
          checkEmptyTokenFocus(inputs[i], currentValue);
        }
      }

      // set border style based on state of "value"
      function checkEmptyTokenFocus(name, value) {
        var id = (name[0] === '#' ? name : '#' + name);
        var p = $(id);
        if (p.length) {
          if (typeof value === 'undefined' || value.length < 1) {
            setInputFocus(p);
          } else {
            clearInputFocus(p);
          }
        }
      }

      // set the highlighting effects on the based element
      function setInputFocus(el) {
        //
        // see code block from earlier in the post
        //
      };

      // clear the highlighting effects on the based element
      function clearInputFocus(el) {
        //
        // see code block from earlier in the post
        //
      };


      // IDs of input elements that should be highlighted if empty
      var FOCUS_INPUTS = ['search_value','target_resources'];

      defaultTokens.on("change:search_value", function(model, value, options) {
        if (typeof value !== 'undefined' && value.replace(/\s/g,"") === "") {
          setToken("form.search_value", undefined, false);
        } else if (typeof value !== 'undefined') {
          if (value !== value.trim()) {
            setToken("form.search_value", value.trim(), false);
          }
        }
        checkEmptyTokenFocusForDashboard(["search_value"]);
      });

      defaultTokens.on("change:target_resources", function(model, value, options) {
        checkEmptyTokenFocusForDashboard(["target_resources"]);
      });

      // helps ensure the input focus is properly set when the search updates
      var populating_resource_search = mvc.Components.getInstance("populating_resource_search");
      populating_resource_search.on("search:done", function(properties) {
        checkEmptyTokenFocusForDashboard(["target_resources"]);
      });
      populating_resource_search.on("search:start", function(properties) {
        checkEmptyTokenFocusForDashboard(["target_resources"]);
      });

      checkEmptyTokenFocusForDashboard(FOCUS_INPUTS);
    });


Lastly, keep in mind that the function checkEmptyTokenFocusForDashboard is called at the end of the file. This is to force the checking of the inputs for highlighting when the dashboard is initially loaded.

The example given here can be extended in many ways. One of the most useful is cleansing user inputs to remove unsafe entries or disallowed characters; for example, preventing the use of the wildcard character "*" in certain scenarios.

Regardless of how simple or complicated the use case, the basic patterns are the same. If you need more examples, check out the various implementations in our app, Layer8Insight App for Splunk.

Upcoming posts will discuss other useful tricks and extensions to SimpleXML dashboards in Splunk.

Stay tuned.


And remember, you can easily and quickly view YOUR enterprise’s user experience data with Layer8Insight. Simply download the software and prepare for a surprise.