Carl Rippon

Building SPAs

Carl Rippon
BlogBooks / CoursesAbout
This site uses cookies. Click here to find out more

Building a Pager Web Component – Part 5: Events

February 07, 2015
javascript

In the last post, we made our pager web component read attributes and respond to attribute changes. In this post we will make our pager component respond to clicking on links and publish a “pageChanged” event.

Exposing function to change pages

Firstly, we are going to create a public function, page() on the component that can be called in order to change pages. The implementation is as follows - it takes in a page number and calls _changePage() if a new page number has been requested.

Proto.page = function(page) {
  if (page) {
    if (this.pageNumber !== page) {
      this.pageNumber = page;
      this._changePage();
    }
  }
  return this.pageNumber;
};

After implementing the function, if you enter document.querySelector("x-pager").page(1); in the console, you will see the page in the pager change to 1.

Creating a page changed event

We are now going to create a pageChanged event in our pager that consumers of the component can subscribe to. We do this using highlighted lines of code.

Proto.page = function(page) {
  var pageChangedEvent;
  if (page) {
    if (this.pageNumber !== page) {
      this.pageNumber = page;
      this._changePage();

      pageChangedEvent = new CustomEvent("pageChanged", {
        detail: {
          pageNumber: this.pageNumber
        },
        bubbles: true,
        cancelable: false
      });
      this.dispatchEvent(pageChangedEvent);
    }
  }
  return this.pageNumber;
};

So, if we add the following script to our consuming page, you will see that the event handler onChangedPage() is invoked when pager.page(2) is executed.

<script>
	(function () {
		var pager,
			onChangedPage;
		pager = document.querySelector("x-pager");

		onChangedPage = function (args) {
			console.log(args.detail);
		};

		pager.addEventListener("pageChanged", onChangedPage.bind(this), false);

		pager.page(2);
	})();
</script>

Handling page clicks

At the moment, clicking on the pager links still doesn’t do anything, so, let’s implement those click handlers. We do this using the highlighted lines of code in the full listing of the pager component below. _addEventListeners() adds _linkClick() as the handler for all the pager links. _linkClick() calls page() that changes the page, which we implemented earlier.

<template>
  <style>
    .x-pager-link {
      margin-right: 6px;
      text-decoration: none;
    }
    a.x-pager-link:hover {
      text-decoration: underline;
    }
    .x-pager-text {
      margin-left: 20px;
    }
  </style>
  <div>
    <a
      href="#"
      title="Go to the first page"
      class="x-pager-link x-pager-first-link"
      >first</a
    >
    <a
      href="#"
      title="Go to the previous page"
      class="x-pager-link x-pager-previous-link"
      >previous</a
    >
    <a
      href="#"
      title="Go to the next page"
      class="x-pager-link x-pager-next-link"
      >next</a
    >
    <a
      href="#"
      title="Go to the last page"
      class="x-pager-link x-pager-last-link"
      >last</a
    >
    <span class="x-pager-text">page x of y</span>
  </div>
</template>

<script>
  (function() {
    var Proto = Object.create(HTMLElement.prototype),
      importDoc = document.currentScript.ownerDocument,
      root,
      pageNumber = 1,
      pageCount = 0;

    Proto.createdCallback = function() {
      var template = importDoc.querySelector("template"),
        clone = document.importNode(template.content, true);
      root = this.createShadowRoot();
      root.appendChild(clone);

      this._readAttributes();
      this._changePage();
      this._addEventListeners();
    };

    Proto.attributeChangedCallback = function(attrName, oldVal, newVal) {
      this._readAttributes();
      if (attrName === "page" || attrName === "page-count") {
        this._changePage();
      }
    };

    Proto.page = function(page) {
      var pageChangedEvent;
      if (page) {
        if (this.pageNumber !== page) {
          this.pageNumber = page;
          this._changePage();

          pageChangedEvent = new CustomEvent("pageChanged", {
            detail: {
              pageNumber: this.pageNumber
            },
            bubbles: true,
            cancelable: false
          });
          this.dispatchEvent(pageChangedEvent);
        }
      }
      return this.pageNumber;
    };

    Proto._readAttributes = function() {
      var pageStr, pageCountStr;
      pageStr = this.getAttribute("page");
      if (!pageStr) {
        pageStr = "0";
      }
      this.pageNumber = parseInt(pageStr, 10);
      pageCountStr = this.getAttribute("page-count");
      if (!pageCountStr) {
        pageCountStr = "0";
      }
      this.pageCount = parseInt(pageCountStr, 10);
    };

    Proto._changePage = function() {
      root.querySelector(".x-pager-text").innerHTML =
        "page " + this.pageNumber + " of " + this.pageCount;
    };

    Proto._linkClick = function(args) {
      var gotoPage;
      if (args.target.innerText === "first") {
        gotoPage = 1;
      } else if (args.target.innerText === "previous") {
        gotoPage = this.pageNumber - 1;
        if (gotoPage < 1) {
          gotoPage = 1;
        }
      } else if (args.target.innerText === "next") {
        gotoPage = this.pageNumber + 1;
        if (gotoPage > this.pageCount) {
          gotoPage = this.pageCount;
        }
      } else if (args.target.innerText === "last") {
        gotoPage = this.pageCount;
      }
      this.page(gotoPage);
    };

    Proto._addEventListeners = function() {
      var links, i;
      links = root.querySelectorAll("a.x-pager-link");
      if (links.length > 0) {
        for (i = 0; i < links.length; i = i + 1) {
          links[i].addEventListener("click", this._linkClick.bind(this), false);
        }
      }
    };

    document.registerElement("x-pager", { prototype: Proto });
  })();
</script>

That’s it for this post. In the next post, we will look at styling our component.

Want more content like this?

Subscribe to receive notifications on new blog posts and courses

Required
© Carl Rippon
Privacy Policy