Filtering Fun with JavaScript. 20 Aug 2014

Ever been in a situation where you have already gathered your results from the server, but you need to do some arbitrary text searching on them? Here's a handy way to do it.

Here it is in action:

See the Pen Filtering Fun with JavaScript by Lorin Tackett (@ltackett) on CodePen.




... and here's the code:

contacts.haml

Basic iteration through all the elements.

%input.form-control{ data: { action: 'text-search' }, type: "text", placeholder: "Filter by name or location" }

.contacts
  - contacts.each do |contact|
    .card{ data: { element: 'item' }}
      %h4= contact.name
      %p=  contact.address
      %p=  contact.citystate

contacts.scss

Simple styling for the cards.

@include 'compass';

body { padding-top: 3em; }

.form-control {
  position: fixed;
  top:      0;
  left:     0;
  right:    0;
  width:    100%;
  padding:  0.75em 0.5em;
  z-index:  1;
}

.contacts {
  position:  relative;
  overflow:  hidden;
  padding:   0.5%;

  &       { font-size: 0; }
  & .card { font-size: initial; }
}

.card {
  @include border-radius(0.25em);
  @include box-sizing(border-box);
  display:    inline-block;
  background: #333333;
  color:      #ffffff;
  margin:     0.5%;
  padding:    2em;
  width:      32.25%;

  &[data-filtered=false] { display: none; }
  &[data-filtered=true]  { display: inline-block; }

  h4, p {
    overflow:      hidden;
    text-overflow: ellipsis;
    white-space:   nowrap;
    line-height:   1.25em;
  }

  h4 { margin: 0 0 0.5em; }
  p  { margin: 0; }
}

text_search.coffee

When I was wiring it up, I chose to use data attr bindings instead of class names. Decoupling your event bindings from your markup classes is a good idea in almost every case.

# Utility function for searching by a word.
hasWordInString = (word) -> @indexOf(word) isnt -1

# Function for filtering items by an array of words
window.filterItems = ($el) ->
  words  = $el.val().toLowerCase()
  $items = $("[data-element=item]")

  # Split `text` to a space-delimited array.
  # We will search by this array later.
  words = words.split(" ")

  # Hide all items on event.
  $items.attr('data-filtered', false)

  # Is the first item of `text` a non-empty string?
  # Cool. Filter the items and show them.
  if words[0].length > 0

    # Iterate through the array of words.
    # If any of those words are found in
    # the `itemText`, this will return `true`.
    # This tells the filter that this item
    # matches the search, which then shows it.
    $items = $items.filter ->
      item = $(this)
      return words.every(hasWordInString, item.text().toLowerCase())
      return

    # Show filtered items
    $items.attr('data-filtered', 'true')

  # Text input is empty? Show all items.
  else $items.removeAttr('data-filtered')

  return


# Bind actions to event handlers
$("[data-action]").each ->
  $el    = $(@)
  action = $el.data('action')

  if action == 'text-search' then $el.on 'keyup', (event) -> filterItems($el)
comments powered by Disqus