Kurt Peek
Kurt Peek

Reputation: 57451

Understanding a jQuery implementation of a search form

I'm working on a project which contains a search form which looks like this:

enter image description here

Here is the HTML (actually, a Django template):

<form action="{% url action %}" method="get" class="left search col s6 hide-on-small-and-down" novalidate>
  <div class="input-field">
    <input id="search" placeholder="{{ placeholder }}"
        autocomplete="off" type="search" name="q"
        value="{{ search_form.q.value.strip|default:'' }}"
        data-query="{{ search_form.q.value.strip|default:'' }}">
    <label for="search" class="active"><i class="material-icons search-icon">search</i></label>
    <i data-behavior="search-clear"
        class="material-icons search-icon"
        {% if not search_form.q.value %}style="display: none;"{% endif %}>close</i>
  </div>
</form>

Here is the concomitant jQuery code:

  $(document).on('click', '[data-behavior="search-clear"]', function(e) {
    $('#search').val('').parents('form').submit();
  });

  $(function() {
    setSearchClearVisibility($('#search'));
  });

  $(document).on('keydown', '#search', function(e) {
    var $el = $(this);

    setTimeout(function() {
      setSearchClearVisibility($el);
    }, 10);
  });

  function setSearchClearVisibility(el) {
    if (el.val() === '') {
      $('[data-behavior="search-clear"]').hide();
    } else {
      $('[data-behavior="search-clear"]').show();
    }
  }

  $(document).on('keydown', '#search', function(e) {
    var $el = $(this);

    setTimeout(function() {
      if (e.keyCode !== 27) return;

      $('#search').val('').blur();
      setSearchClearVisibility($el);

      if ($el.data('query') !== '') {
        $('#search').parents('form').submit();
      }
    }, 10);
  });

I have a couple of questions about the jQuery code:

Firstly, it seems like the setSearchClearVisibility() function could be refactored as follows:

  function setSearchClearVisibility(el) {
    if (el.val() === '') {
      $('[data-behavior="search-clear"]').toggle();
    }
  }

Is this indeed equivalent?

Secondly, it would also seem that the line

$(document).on('keydown', '#search', function(e) {

could be replaced by

$('#search').keydown(function(e) {

or not?

Thirdly, I'm not sure why there is a 10-ms setTimeout in a couple of places. Why not perform the action immediately?

Fourthly, I'm puzzled by the line

if (e.keyCode !== 27) return;

The key code 27 corresponds to the Escape key; would this not mean that if the key pressed is not the Escape key, the function would return and the code below it (the toggling of the close icon visibility and submission of the form) would not be executed? I also don't notice any difference in behavior if I comment out this line.

Upvotes: 2

Views: 48

Answers (1)

tao
tao

Reputation: 90068

1. No, they're not equivalent.

The code you propose will toggle (change from existing state to the other) if the condition is true and will not perform any change if the condition is not met.
The code you have now is: hide if condition, show if not.

Passing a condition to toggle will show if true and hide if not. So this is equivalent:

function setSearchClearVisibility(el) {
  $('[data-behavior="search-clear"]').toggle(el.val() !== '');
}

2. $(parent).on(event, child, callback) is not the same as $(child).on(event, callback)

And this is really important.
The first only binds once, on the parent for all existing or future children meeting the child selector. The evaluation (of child selector) is done when event happens.
The second binds once per child. So if you have 1k children, you have 1k bindings, instead of one. And the evaluation of selector is done at the time of binding, not of the event. So if you add more children after the binding, they won't have the callback bound on event.

3. setTimeout(callback, delay) is to queue the execution of callback at the latest between:

  • end of current execution queue
  • current time + delay

See this answer for details. setTimeout(callback) is a typical way to queue execution of callback at the end of the execution queue. It ensures current function/scope/thread finishes execution before callback is run.

4. if (e.keyCode !== 27) return; means:

if the pressed key was not escape, exit function. Else, proceed and run what's after me (till end of function). Please note this function only gets executed on keydown event on #search. So, to test it, you'd have to have #search focused/active/selected and press a key. If the key pressed is escape, the code after the condition will get executed. If it's any other key, it won't.

Upvotes: 4

Related Questions