constructor(options: OptionsType) {
    let empty: boolean = false;

    if (typeof options !== 'object') {
      throw new Error(errors.optionsObject);
    }

    // If this.options.find is missing, the searching won't work so we'll thrown an exceptions
    if (typeof options.find !== 'string') {
      throw new Error(errors.findOption);
    }

    const defaults = {
      input: 'input[type=search]',
      find: '',
      placeholder: undefined,
      mark: false,
      class: {
        visible: undefined,
        hidden: 'hidden'
      },
      dynamic: false,
      minCharacters: 0,
      hiddenAttr: false,
      shouldShow: stringIncludes,
      onHidden: undefined,
      onVisible: undefined,
      onEmpty: undefined,
      onFound: undefined,
      onInput: undefined
    };

    /**
     * @member options
     * @type {Object}
     * @memberOf holmes
     */
    this.options = Object.assign({}, defaults, options);
    this.options.class = Object.assign({}, defaults.class, options.class);

    /**
     * The amount of elements that are hidden
     * @member hidden
     * @type {Number}
     * @memberOf holmes
     */
    this.hidden = 0;

    /**
     * The input element
     * @member input
     * @type {NodeList}
     * @memberOf holmes
     */
    /**
     * All of the elements that are searched
     * @member elements
     * @type {NodeList}
     * @memberOf holmes
     */
    /**
     * Placeholder element
     * @member placeholderNode
     * @type {Element}
     * @memberOf holmes
     */
    /**
     * Is the current instance running
     * @member running
     * @memberOf holmes
     * @type {Boolean}
     */
    this.running = false;

    window.addEventListener('DOMContentLoaded', () => this.start());

    /**
     * Execute a search, this can be used in combination with .setInput()
     * @function search
     * @memberOf holmes
     */
    this.search = () => {
      // Input has started to be listened to
      this.running = true;

      // By default the value isn't found
      let found: boolean = false;

      /**
       * Lowercase string holmes searches for
       * @member searchString
       * @memberOf holmes
       * @type {string}
       */
      this.searchString = this.inputString();

      // If a minimum of characters is required
      // check if that limit has been reached
      if (this.options.minCharacters) {
        if (this.searchString.length !== 0) {
          if (this.options.minCharacters > this.searchString.length) {
            return undefined;
          }
        }
      }

      // If the dynamic option is enabled, then we should query
      // for the contents of `elements` on every input
      if (this.options.dynamic) {
        this.elements = document.querySelectorAll(this.options.find);
        this.elementsLength = this.elements.length;
        this.elementsArray = Array.prototype.slice.call(this.elements);
      }

      if (this.options.mark) {
        /**
         * Regex to remove <mark>
         * @member _regex
         * @memberOf holmes
         * @private
         * @type {RegExp}
         */
        this._regex = new RegExp(`(${this.searchString})(?![^<]*>)`, 'gi');
      }

      // Loop over all the elements
      this.elementsArray.forEach((element: HTMLElement) => {
        // If the current element doesn't contain the search string
        // add the hidden class and remove the visible class
        if (
          this.options.shouldShow(
            element.textContent.toLowerCase(),
            this.searchString
          )
        ) {
          this._showElement(element);

          if (empty && typeof this.options.onFound === 'function') {
            this.options.onFound(this.placeholderNode);
          }
          // The element is now found at least once
          found = true;
        } else {
          this._hideElement(element);
        }
      });

      if (typeof this.options.onInput === 'function') {
        this.options.onInput(this.searchString);
      }

      // No results were found and last time we checked it wasn't empty
      if (found) {
        if (this.options.placeholder) {
          this._hideElement(this.placeholderNode);
        }
      } else {
        if (this.options.placeholder) {
          this._showElement(this.placeholderNode);
        }

        // Empty means that there are no results
        // if the situation isn't yet empty
        // so it's the first time seeing the placeholder
        // we'll emit the onEmpty function
        if (empty === false) {
          empty = true;
          if (typeof this.options.onEmpty === 'function') {
            this.options.onEmpty(this.placeholderNode);
          }
        }
      }
    };
  }
Search for dom elements on your page

For legacy reasons, this class is a <code>function</code>, and thus doesn't have a capital
You can fix this by using <code>var Holmes = holmes</code> before doing <code>new Holmes</code>.

@class holmes
@constructor
@param {string} [options.input='input[type=search]']
  A <code>querySelector</code> to find the <code>input</code>
@param {string} options.find
  A <code>querySelectorAll</code> rule to find each of the find terms
@param {string=} options.placeholder
  Text to show when there are no results (innerHTML)
@param {bool} [options.mark=false]
  Whether to <code>&lt;mark&gt;&lt;/mark&gt;</code> the matching text
  Text to show when there are no results (<code>innerHTML</code>)
@param {string} [options.class.visible=false]
  class to add to matched items
@param {string} [options.class.hidden='hidden']
  class to add to non-matched items
@param {boolean} [options.dynamic=false]
  Whether to query for the content of the elements on every input.
  If this is <code>false</code>, then only when initializing the script will
  fetch the content of the elements to search in. If this is <code>true</code>
  then it will refresh on every <code>input</code> event.
@param {boolean} [options.contenteditable=false]
  DEPRECATED (now handled automatically) whether the input is a contenteditable or
  not. By default it's assumed that it's <code>&lt;input&gt;</code>, <code>true</code> here
  will use <code>&lt;div contenteditable&gt;</code>
@param {boolean} [options.instant=false]
  DEPRECATED!!, after v1.13.3 use <code>holmes({}).start();</code> instead
  By default Holmes waits for the <code>DOMContentLoaded</code> event to fire
  before listening. This is to make sure that all content is available. However
  if you exactly know when all your content is available (ajax, your own event or
  other situations), you can put this option on <code>true</code>.
@param {number} [options.minCharacters=0] The minimum amount of characters to be typed before
  Holmes starts searching. Beware that this also counts when backspacing.
@param {boolean} [options.hiddenAttr=true]
  Adds <code>hidden="true"</code> to hidden elements. interesting
  <a href="https://www.paciellogroup.com/blog/2012/05/html5-accessibility-chops-hidden-and-aria-hidden/">
  link</a> explaining its use.
@param {function} [shouldShow]
  A custom matching function to be called with as first argument the text of an element,
  and as second argument the current input text. This should return true if you want the
  element to show, and false if it needs to be hidden
@param {onChange} [options.onHidden]
  Callback for when an item is hidden.
@param {onChange} [options.onVisible]
  Callback for when an item is visible again.
@param {onChange} [options.onEmpty]
  Callback for when no items were found.
@param {onChange} [options.onFound]
  Callback for when items are found after being empty.
@param {onInput} [options.onInput]
  Callback for every input.