Search animation with HTML5, CSS 3 and Angular 7

Spread the love
  • 3
    Shares

The data-driven application, requires strong search capabilities. Search interface thus become an important element in the application. Ideally search feature has to be easily accessible. Given that real estate in GUI applications weighs like a Gold, it is necessary that the Search element should be unobtrusive.

Photo by Elijah O’Donnell on Unsplash

Need

I was looking for a search component – Which should meet following criterias

  • Occupies less space
  • Can be placed at a convenient location
  • Generic enough so that it can be reused any part of the application.
  • Lightweight, no/less dependency on other libraries.
  • Easily pluggable with Angular 7

The chase begins

Thankfully we are living in an information abundant era. I googled for a “Search component” and it brought too many results. I realized that there are tons of ways and styles to achieve what I want. I finally settled down on the following approach.

  • Use Plain HTML & CSS.
  • I already have jQuery installed as a dependency in my project. So leverage that as much as possible.
  • Use CSS animation for the smooth transition.
  • Use Event Emitter to generalize the search (This is purely optional and can be customized as per your project requirement or framework. I am using Angular, so I did this in Angular way.)

In short, I decided to go for building a simple search component without having a need to add one more third-party dependency.

HTML Skeleton

The HTML definition part is pretty simple, we need to place the controls one after the other in a row mode. Refer to the below snippet


<div id="search-container">
  <div class="searchFields">
    <mat-form-field>
      <input matinput="" #searchinput="" placeholder="Search..." (keyup.enter)="search()" autocomplete="off">
    </mat-form-field>
    <button mat-icon-button="" class="submitButton" (click)="search()">
      <mat-icon>search</mat-icon>
    </button>
  </div>
  <button type="button" mat-icon-button="" class="toggleBtn" (click)="toggleSearch()">
    <mat-icon>{{searchIcon}}</mat-icon>
  </button>
</div>

We have parent container (search-container) with two children – searchFields and a toggleBtn. The input field and a button next to it will be displayed when a user clicks toggleBtn.

Note that we are also making use of keyup.enter event to capture enter key.

Search appearance (SCSS)

Initially the search component will only display a button with a magnifier button. The searchFields is hidden from the view.


#search-container {
    width: 40px;
    position: relative;
    overflow: hidden;
    transition: width 0.5s ease;
    display: flex;
    align-items: center;
    background-color: #fff;
}

The container is set to the equal width of the toggleBtn. A button with only an icon has a width of 40 pixels. The overflow property does the magic of hiding the elements from the view.

The additional CSS attributes handle the layout of the container. We are also using transition property to ensure that the animation will have a smooth pleasing effect.

#search-container .searchFields { display: flex; align-items: center; flex-direction: row; padding: 0px 50px; }

SearchFields div is a container in itself as it holds an input control along with a button to initiate a search. Among all the properties, the padding values are very important. It pushes the components outside of the view when searchContainer is in collapsed mode.

We must proceed with setting the position for the toggleBtn. This involves a little bit of trick.


#search-container .toggleBtn {
        position:absolute;
        right: 0;
}

We are positioning the toggle button with absolute value. The values ensure that no matter what happens the toggleBtn will stick to the right of the container.

Now the final piece in stylesheet – class to set width of the search container in expanded mode.


#search-container.open {
        width: 300px;
}

Search component behavior

Believe me, this is going to be an easiest part. When user clicks the toggleBtn, we need to only toggle CSS class and emit an event. I have defined two helper functions inside search-component.ts file.


private toggleClass(elem, className) {
    this.hasClass(elem, className) ? elem.classList.remove(className) : elem.classList.add(className);
  }

  private hasClass(elem, className): boolean {
    return elem.classList.contains(className);
  }

The code has nothing to do with Angular specific APIs, rather it is core JavaScript DOM functions. The classList attribute of a DOM node is quite handy to query the current state and add/remove a class. We will leverage these common functions in the event handler of toggleBtn click


toggleSearch() {
    const searchContainer = document.getElementById('search-container');
    this.toggleClass(searchContainer, 'open');
    this.searchIcon = this.hasClass(searchContainer, 'open') ? 'clear' : 'search';
    if (!this.hasClass(searchContainer, 'open') &amp;&amp; this.interactedWithSearch) {
      this.searchEvent.emit({ action: 'CLEAR' });
      this.interactedWithSearch = false;
      this.searchInput.nativeElement.value = '';
    }
  }

To begin with we get reference of the search container and then we toggle the class to get the desired effect.

The searchIcon variable is reset to the desired value to ensure that we get nice desired effect of shuffling search button icon from magnifier to clear and back to magnifier.

The next set of lines, emit a CLEAR event and reset the value of the input field. This is necessary in case a search container is collapsed by a user. Here are the variable definitions for the search component.


searchIcon = 'search';

  @ViewChild('searchInput', { read: ElementRef })
  private searchInput: ElementRef;

  interactedWithSearch = false;

  @Output()
  searchEvent = new EventEmitter<{ query?: string, action: 'SEARCH' | 'CLEAR' }>();

Embed Search

Now that the Search component has been defined, we must use it in our app component.


<nav class="navbar">
<app-search (searchEvent)="handleSearch($event)"></app-search>
</nav>
<br>
You searched for = {{searchTerm}}

I have created a navigation bar and embedded the search component as a child. The searchEvent is captured and passed to the handleSearch function along with a special keyword $event. The keyword will help to query the output generated by the search component.

Finally we display the text entered by user using searchTerm variable. Refer to following block for handleSearch definition.

AppComponent Behavior


searchTerm = '';
  handleSearch(r) {
    if (r.action === 'SEARCH') {
      this.searchTerm = r.query;
    }
  }

The handleSearch function simply checks the attribute of the data received and assigns the text received from the search component to a variable. You can find the entire source code at stackblitz.

Leave a Comment.