A Pure ECMAScript 6 Search Box Class

In the last four days I’ve walked through the bubble search example in ECMAScript 6 with all the latest stuff. The only thing I left out was testing, which takes a considerable setup right now. I started wondering two things:

  1. Could I write the Javascript Class in pure ECMAScript 6 with no jQuery?
  2. Could I load that class into my HTML file without needing transpiling?

Starting with my empty ASP.NET project that handles just web files, I started again. This time – all my work was in wwwroot.

Getting rid of jQuery

There are a number of things I do using jQuery and I needed to handle all of them. Here is a big long list:


I need to compose a new object based on defaults and the object that is passed in by the user – this allows the user to over-ride options that I have set. Right now I use $.extend like this:

this.options = $.extend({
    searchId: MySearchBox.generateGUID(),
    bubbleClass: "bubble"
}, pOptions || {});

In ECMAScript 6 I can use the Object.assign() method, so this becomes:

this.options = Object.assign({
    searchId: MySearchBox.generateGUID(),
    bubbleClass: "bubble"
}, pOptions);

This is pretty much a straight swap-out of the jQuery version. This requires the Babel polyfill to be included – you can just include it in the script section of your HTML page so it doesn’t pollute your code.

DOM Creation

I need to create DOM elements – most notably the div and associated contents for the search bubble. This can be achieved using standard Javascript elements. First, the old:

let divElement = $(`<div id="${options.searchId}" class="${options.bubbleClass}"></div>`)
    .html("<div class='search'><input type='search' name='serch' placeholder='Search'></div>")

Out with the old and in with the new:

let body = document.querySelector("body");
let divElement = document.createElement("div");
divElement.id = options.searchId;
divElement.className = options.bubbleClass;
divElement.innerHTML = "<div class='search'><input type='search' name='serch' placeholder='Search'></div>";

This is a little more intense than the old version but has the advantage of being easily abstracted into a function.

Adding CSS Styling

Most of the CSS is handled by the style sheet, but positioning is handled dynamically. In the old version, I did this:

    "display": "block",
    "position": "absolute",
    "left": left + "px",
    "top": top + "px",
    "width": width + "px"

Without jQuery this becomes:

divElement.style.display = "block";
divElement.style.position = "absolute";
divElement.style.left = left + "px";
divElement.style.top = top + "px";
divElement.style.width = width + "px";

I could abstract this to a style merge function using Object.assign but this is infrequent enough that I can just inline it.

element.position() and element.height()

I use element.position() to get the top and left element positions and element.height() to get the height, used in calculating the best place to put the div element. These can be easily replaced like this:

let left = el.offsetLeft;
let top = el.offsetTop;
let width = el.offsetWidth;
let height = el.offsetHeight;

Removing an element

I can remove an element easily enough:


Handling events

I have a click event that I need to register on the element I am passed. This can be handled easily enough using standard addEventListener code. I can also wire up the object appropriately using bind() providing I don’t want to remove the handler. For example:

pElement.addEventListener("click", this.onClick.bind(this), false);

In short – everything is “doable” so let’s take a look at the new class:

 * Represents a Bubble-style Search Box
export class SearchBox {
     * Construct a new Bubble-style Search Box and attach it to the click event of the
     * given element
     * @constructor
     * @param {HTMLElement} pElement - The element to attach to
     * @param {Object} pOptions - List of options to override
    constructor(pElement, pOptions = {}) {
        this.element = pElement;
        this.options = Object.assign({
            cssClass: "bubble",
            maxWidth: 350
        }, pOptions);

        this.searchel = null;
        this.element.style.cursor = "pointer";
        this.element.addEventListener("click", this.onClick.bind(this), false);

     * Handle the onClick Event for the element that this search box is attached to
    onClick() {
        if (this.searchel != null) {
            // Search Box is available so we need to delete it
            this.searchel = null;
        } else {
            // Search Box is not currently available, so create it
            // First, calculate the position and size of the box
            let left = this.el.offsetLeft - 22;
            let top = this.el.offsetTop + this.el.offsetHeight + 12;
            let bodyRect = document.body.getBoundingClientRect();
            let width = ((left + this.options.maxWidth) < bodyRect.width) ? this.options.maxWidth : bodyRect.width - left;

            // Now create the search box element in the DOM
            this.searchel = document.createElement("div");
            this.searchel.id = this.generateGUID();
            this.searchel.className = this.options.cssClass;
            this.searchel.style.position = "absolute";
            this.searchel.style.left = left + "px";
            this.searchel.style.top = top + "px";
            this.searchel.style.width = width + "px";
            this.searchel.innerHTML = "<div class='search'><input type='search' name='serch' placeholder='Search'></div>";

            // Attach the search box element to the BODY element of the DOM
            // Set focus to the search box

     * Generate a Unique ID for the search box
     * @returns {string} The Unique ID
    generateGUID() {
        let d = new Date().getTime();
        return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
            let r = (d + Math.random() * 16) % 16 | 0;
            d = Math.floor(d / 16);
            return (c === "x" ? r : (r & 0x3 | 0x8)).toString(16);

     * Public function to get the searchBox element.  Note that this will be null
     * if the search box is not shown right now
     * @return {HTMLElement} The search box element
    get searchBox() {
        return this.searchel;

     * Public function to get the element we are attached to
     * @return {HTMLElement} The element this object is attached to
    get el() {
        return this.element;

I have also created a wwwroot/js/app.js file that looks like this:

import { SearchBox } from "./classes/SearchBox";

function appBootStrap() {
    new SearchBox(document.querySelector("#nav-search")); 

if (document.readyState !== "loading") {
} else {
    document.addEventListener("DOMContentLoaded", appBootStrap);

The next step is loading everything up. For this, I need my HTML file:

<!DOCTYPE html>
    <meta charset="utf-8">
    <title>Test Page</title>
    <link rel="stylesheet" href="./css/site.css">
                <li id="nav-search">Search</li>

    <script src="lib/traceur/js/traceur.js"></script>
    <script src="lib/es6-module-loader/js/es6-module-loader.js"></script>

I’ll leave it as an exercise to get traceur and es6-module-loader in the right place. Traceur is “the other transpiler” and the es6-module-loader library likes it better. The ES6 Module Loader loads ES6 formatted modules. Between the two of them we should be able to load ES6 files in the browser just like we would do with ES5. Getting these files in the right place is relatively simple given what I’ve shown about gulp and npm recently.

Now it’s just a case of bringing in the styling from the old project and my page is complete. I did have to copy SearchBox.less into the src/less directory (along with the search-32.png) and alter the Gulpfile.js accordingly.

I’ve uploaded this code to my GitHub Repository for you to review. Note that it is the complete code, so check out the other articles in the series that cover everything.

At this point, this code is not a viable solution for production code. Transpiling on the fly does not lead to good performance in the browser. However what I have proved here is that jQuery is no longer the necessity it once was, at least for components like this. Also, you can run ES6 code in the browser.

My other requirement for web components is embedded style sheets. This version still compiles the style sheets together which breaks my nice clean code separation that I am looking for. So I’m still looking for the perfect component technology.