JavaScript - - By Rakhitha Nimesh

Building an Image Gallery Component with Polymer

Web components are becoming the future trend of Web application development. They allow us to group HTML markup, scripts, and styles into a reusable component. These components are part of the browser and hence don’t need external JavaScript libraries like jQuery to provide its functionality. As reported by Wikipedia,

Web Components are a set of standards currently being produced by Google engineers as a W3C specification that allow for the creation of reusable widgets or components in web documents and web applications. The intention behind them is to bring component-based software engineering to the World Wide Web. The components model allows for encapsulation and interoperability of individual HTML elements.

In short, web components resolve the complexity of elements in a Web page and provide a simpler and easily understandable element structure. HTML already provides sets of built in tags such as headers, paragraphs, lists and so on. However, in some cases the existing tags are not enough to provide the right support to large Web applications and here is where Web components come to the rescue. Some libraries, most notably Polymer, are making web components usable in non supporting browsers with Polyfill Web Components.

In this tutorial, we’re going to learn how to create an Image Gallery component with Polymer version 1.0. All the code provided in this article is available on GitHub.

Introduction to Polymer

Image galleries are commonly used in the development of websites. Generally, developers tend to use existing gallery libraries and they often have to face the boring problem of the complexity of the element structure generated by these libraries. The following code previews the structure of a very basic image gallery.

<div id="gallery-panel" class="gallery-panel">
    <div class="slides">
        <div id="links" name="links">
            <img src="images/thumbnails/img01.jpg" alt="Title 1">
            <img src="images/thumbnails/img02.jpg" alt="Title 2">
            <img src="images/thumbnails/img03.jpg" alt="Title 3">
            <img src="images/thumbnails/img04.jpg" alt="Title 4">
            <img src="images/thumbnails/img05.jpg" alt="Title 5">
        </div>
    </div>
    <div class="modal fade">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header"> <a class="close"><button type="button" class="close" aria-hidden="true"  >X</button></a>

                     <h4 class="modal-title"></h4>

                </div>
                <div class="modal-body next">
                    <img class='modal-img' />
                </div>
                <div class="modal-footer">
                    <button id="previous" type="button" class="btn btn-default pull-left prev">Previous</button>
                    <button id="next" type="button" class="btn btn-default next">Next</button>
                </div>
            </div>
        </div>
    </div>
</div>

Even though this is a code snippet which shows how the structure of a basic image gallery looks like, it’s clear that it isn’t as simple as it could be. We might have to repeat the same set of code for each gallery in a web page, so the web page become very large and difficult to manage.

Here is where and how Polymer provides its solution through the use of web components. Polymer is a library created to build custom web components. There are several libraries useful to create web components, but browser compatibility is an issue that most libraries have not solved yet. Polymer provides the best solution by providing polyfills which are able to manage web components in non compatible browsers. You can discover more about Polymer and its usage here.

When Polymer is used to build the gallery as a web component, our code should look like the following.

<simple-gallery>
    <img src="images/thumbnails/img01.jpg" alt="Title 1">
    <img src="images/thumbnails/img02.jpg" alt="Title 2">
    <img src="images/thumbnails/img03.jpg" alt="Title 3">
    <img src="images/thumbnails/img04.jpg" alt="Title 4">
    <img src="images/thumbnails/img05.jpg" alt="Title 5">
</simple-gallery>

As you can see, our code has become much simpler and it has just the essential tags. All other complex coding is hidden from the user, making it highly reusable. At this point, let’s start building the gallery component with Polymer.

How to Install Polymer

Polymer can be installed with all its dependencies using Bower running the following command:

bower install --save Polymer/polymer#^1.1.0

This will install the Polymer library and the web component polyfills inside a folder called bower_components.

Before taking a step forward, we have to identify the features needed for a basic image gallery. We’re planning to create a web component for an image gallery and we’ll need to create a separate file for the gallery component. Below, you can find a list of points we have to take into account to build our gallery correctly:

  • HTML Markup to display gallery images;
  • HTML Markup for modal popup with large images;
  • CSS styles for the gallery component;
  • Buttons for Next, Previous and Close events;
  • JavaScript to handle gallery open, close and traverse.

Unlike normal web pages, all of these things are defined inside a single component file. So all the gallery code is independent from other components and reusable across multiple places. Said that, we can start building the main web page and image gallery component.

First we have to create the main page of our website to include the web components. We can create a page called index.html inside the project root folder. Then we need to include the necessary files for Polymer and the HTML markup for the image gallery. The following code snippet shows how to proceed:

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>Polymer</title>
        <script src="bower_components/webcomponentsjs/webcomponents.js"></script>
        <link rel="import" href="bower_components/polymer/polymer.html">
        <link rel="import" href="simple-gallery.html">
    </head>
    <body>
        <simple-gallery>
            <img data-original="images/img01.jpg" data-index='s1' src="images/thumbnails/img01.jpg" alt="Title 1">
            <img data-original="images/img02.jpg" data-index='s2' src="images/thumbnails/img02.jpg" alt="Title 2">
            <img data-original="images/img03.jpg" data-index='s3' src="images/thumbnails/img03.jpg" alt="Title 3">
        </simple-gallery>
    </body>
</html>

First, we need to import the polymer.html file from the bower_components/polymer folder using the link element. Then, we need to add a file called webcomponents.js from thebower_components/webcomponentsjs folder. This is the file responsible for handling custom elements in non compatible browsers. If you are familiar with Polymer 0.5, you may know this file as platform.js. This has been replaced in the latest 1.0 version and now a file calledwebcomponents.js handles the same functionality. Finally, we need to import our custom gallery component using a link element. In our example, we have named our custom component simple-gallery.

Now, we can start defining our layout structure using some HTML tags and web components. We have added a custom component called simple-gallery with all the gallery images inside the opening and closing tags. You might notice that we have used few data attributes called data-original and data-index. These attributes are used to simplify the process of handling the URL of the original images and the index of the images in the gallery. Of course, it’s also possible to create a gallery even without those data by manipulating the DOM.

We have imported the necessary files into our main page and now we are ready to create the gallery component. The first step is to create a new file inside the project folder called simple-gallery.html for the gallery component. We can add the following code to define the structure of our gallery component:

<link rel="import" href="bower_components/polymer/polymer.html">

<dom-module id="simple-gallery" >
   <style>
      /* Styles for the Gallery Component */    
   </style>
   
   <script>
      HTMLImports.whenReady(function () {
          (function () {
              var current_index = 0;
              var image_length = 0;

              Polymer({
                  is: "simple-gallery",
                  ready: function () {},
                  load_popup: function (e, detail, sender) {},
                  next: function () {},
                  prev: function () {},
                  close: function () {},
              });
          })();

      });
   </script>
     
   <template></template>
 </dom-module>

Firstly we have to include the polymer.html file as usual. Then, we start defining the gallery component. Polymer 1.0 uses the dom-module element to define new components. The component name should be used as the id attribute of the dom-module element.

We use the style element to define all the styles needed for our component. Even this one has changed from version 0.5, where we used style inside the template element. In version 1.0, it’s now placed outside the template element as an independent tag. The HTML markup for the gallery component goes inside the template element. Finally, we can initialize the Polymer element inside the HTMLImports.whenReady callback function. This function ensures that all the imported documents are loaded before executing the code.

We have to use the is attribute with component name (simple-gallery) to register a Polymer component. This procedure is different from the one employed using version 0.5 where we had the following syntax:

Polymer('simple-gallery', {});

We have to define the necessary functions for our custom component. We have five functions: ready, load_popup_, prev, next and close. Let’s identify their functionality one by one:

  • ready: This is a Polymer function which executes once the Polymer object is ready. We use this function to initialize any features in our component.
  • load_popup: This function is used to load the original image in a modal popup window.
  • prev: This functions is used to traverse backwards through the gallery images
  • next: This function is used to traverse forwards through the gallery images
  • close: This function is used to close the modal popup window

In the next section, we can start the implementation of our gallery component based on the structure we’ve created in this section.

Templates are among the most important parts of a web component. In this section we’ll see which is the markup that gets displayed to the end user. The content inside the template is not visible to the end user since users will only see the simple-gallery tag in the source code and other inner elements will only be visible with special browser tools.

At this point, we have to implement both the HTML to display the images and the HTML for the modal popup inside this template tag. Let’s look at the code for our gallery component template:

<div id="gallery-panel" class="gallery-panel">
    <!-- The container for the modal slides -->
    <div class="slides">
        <div id="links" name="links"></div>
    </div>
    <!-- The modal dialog, which will be used to wrap the lightbox content -->
    <div class="modal fade">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header"> <a class="close"><button type="button" class="close" aria-hidden="true" on-click="close" >X</button></a>

                     <h4 class="modal-title"></h4>

                </div>
                <div class="modal-body next">
                    <img class='modal-img' />
                </div>
                <div class="modal-footer">
                    <button id="previous" type="button" class="btn btn-default pull-left prev" on-click="prev">Previous</button>
                    <button id="next" type="button" class="btn btn-default next" on-click="next">Next</button>
                </div>
            </div>
        </div>
    </div>
</div>

The component template contains two parts. The first one is defined with a div element having class slides. This container will be filled with the images we provide from the simple-gallery component. This template part is visible to the user by default. The second part starts with the div with class="modal fade". This will be used for modal popup window and hence hidden from the user by default. We have also the necessary buttons for previous, next and close with Polymer on-click call. Polymer uses on-event syntax to define events on elements.

Now that we have all the necessary markup for our template, our next task will be to style our elements inside the style tag. If needed, you can also import external stylesheets into components. We’re not going to include all the styles in here. You can find all the styles for our component inside the source code folder. The following code contains some sample styles for our component.

We have used normal CSS classes and selectors. So these selectors will become descendent selectors of the gallery element. For example, .modal class will be written as .modal.simple-gallery. You can use Polymer :host to target specific component elements and its ancestors. You can find more information about how to style components here.

We defined five functions for our gallery component in the previous section. Let’s start implementing those functions one by one.

The ready Function

We use the ready function to get the images defined inside the simple-gallery element and add them to the #links container inside our template. The following code contains the implementation of this first function.

ready: function () {
    var images = Polymer.dom(this).querySelectorAll('img');
    var container = this.$.links;

    for (var img in images) {
        images[img].addEventListener('click', this.load_popup);
        container.appendChild(images[img]);
    }
}

We select all the images inside the component by using the querySelectorAll function on the Polymer.dom(this) object. Then we traverse through each element and add it to the #links container while defining a custom click event to call the load_popup function.

The load_popup Function

This function is used to open the modal popup and display the original image when clicking on an image from the gallery. The following code shows how to implement it:

load_popup: function (e, detail, sender) {
    e.preventDefault();
    var links = document.getElementById('links');
    image_length = links.getElementsByTagName('img').length;

    var image_url = e.target.getAttribute('data-original');
    var modalbody = document.getElementsByClassName("modal-body")[0];
    var modal_img = modalbody.getElementsByTagName('img')[0];
    modal_img.setAttribute("src", image_url);
    var modal = document.getElementsByClassName("modal")[0];
    modal.style.display = 'block';

    current_index = parseInt(e.target.getAttribute('data-index').replace("s", ""));
    return false;
}

We can get the clicked image using e.target. Then we grab the original image URL using the data-original attribute and use some DOM manipulation to add the image into the modal window and open the modal window for the user. We can also store the index of the selected image using the data-index attribute. Generally, we tend to use libraries such as jQuery for this type of DOM manipulation. However, we have used plain JavaScript functions in this example and I’ll explain the reasons that brought me to avoid using jQuery in the next section.

The next Function

This function is used to traverse through the gallery images in popup window. Once the popup is opened, we can use the Next and Previous buttons to traverse through the gallery instead of clicking each thumbnail from the gallery. Let’s look at the implementation of the next function.

next: function () {
    current_index = current_index + 1;
    if (current_index == (image_length + 1)) {
        current_index = 1;
    }
    var current_image = document.querySelectorAll("[data-index='s" + current_index + "']");
    image_url = current_image[0].getAttribute('data-original');
    var modalbody = document.getElementsByClassName("modal-body")[0];
    var modal_img = modalbody.getElementsByTagName('img')[0];
    modal_img.setAttribute("src", image_url);
}

We use the current index generated from the load_poup function to get the next image from the gallery. The image is identified by thedata-original attribute and replaced into the existing modal window image. This process continues and once we reach the end, the index is set to 1 to start from the beginning. The implementation of the prev function is also similar to this one and hence will not be included here. You can find it inside the source code folder.

With this last snippet, we have completed the basic image gallery component with Polymer. You can use the source code files to see how it works. They are available here.

The image gallery is a common component in most websites. So, there are large amount of pure JavaScript as well as jQuery libraries you can use to create your image galleries. You might be wondering why we should create an image gallery instead of converting a jQuery image gallery into a web component. It would be the easier and better choice. However, the problem is that many jQuery plugins don’t work with Polymer as web components. These plugins often generate conflicts and hence we have to build them from scratch.

It’s important to identify the reason for not recommending jQuery plugins or jQuery code inside Polymer web components. There are two types of DOM elements called Local DOM and Shadow DOM:

  • Local DOM : these elements are visible to the user. In this scenario, all the images inside our gallery component is part of the Local DOM;
  • Shadow DOM: these elements are not visible to the user and generated by a web component. In this scenario, image gallery container and popup window is the shadow DOM.

Most jQuery plugins work on Local DOM and expect the elements to be in the Local DOM. But the elements generated from a web component are placed in the Shadow DOM, hence jQuery plugins can’t identify these elements. For this reason, it’s not recommended to use jQuery plugins or jQuery code inside web components. I suggest you to use plain JavaScript function to replace the jQuery functionality instead. This might seem a limitation considering the number of available jQuery plugins, but web components are build at a rapid pace and soon we’ll see them replacing most jQuery plugins.

Conclusions

We expect web components to be the future of application development thanks to their powerful way of creating and managing web pages with unnecessary complexity. However, their implementation is still in an early stage and yet to become a strict standard. Even though libraries like Polymer make it possible to use these components in non compatible browsers, you might still find issues, especially in mobile browsers.

It’s up to you and your specific case to decide to use them in a real application or not. Personally, I hope that web components will become widely used very soon.

Sponsors