Getting Started

Product Compare is a library for product features, and product plans comparison. It supports multiple CSS frameworks and can be implemented in any HTML layout using data attributes. The library contains an embedded touch-enabled gallery, supports local and session storage, and can compare by text, HTML, or numbers.


Quick Start

This part is a self-contained quick introduction to the Product Compare library. Please check other sections for more details about different topics.

The library layout is very flexible and can be configured through various data attributes. Each data attribute defines the specific functionality of the HTML element. This way, users can match the widget's styles and layout to the look and feel of their website.


The full list of HTML data attributes can be found here.


Below you can find getting started tutorials of some widely used CSS frameworks:


If your CSS framework is missing, or you prefer pure CSS, please check the following tutorial.


Bootstrap Example

This tutorial will demonstrate how to implement the Product Compare Library with Bootstrap CSS Framework.

Get started with a minimal Bootstrap layout as described in Bootstrap 5 documentation:

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">

    <title>Example</title>
  </head>
  <body>



    <!-- Bootstrap Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
  </body>
</html> 

You can use any CSS Framework or your custom HTML & CSS.



Create a container that will hold all content. The container should have data-jso-pcompare attribute.

<div class="container position-relative" data-jso-pcompare>

</div>

You may find other ways to define containers here.


Create a gallery layout inside the container:

<div class="container position-relative" data-jso-pcompare>

    <!-- gallery -->
    <div class="row">
        <div 
            class="col d-flex align-items-center position-relative mb-4" 
            data-jso-gallery>

            <!-- left button -->
            <button 
                data-jso-left-btn 
                type="button" 
                class="btn btn-dark position-absolute px-3 d-none d-lg-inline-block rounded-pill" 
                style="left: -3rem">&laquo;</button>

            <!-- gallery content -->
            <div class="flex-grow-1 overflow-hidden">
                <div class="row flex-nowrap" data-jso-gallery-holder>

                    <!-- gallery item #1 -->
                    <div class="col-12 col-md-6 col-lg-3" data-jso-item="1">
                        <div class="card">
                            <div class="card-body">
                                <h5 class="card-title mb-2">Starter</h5>
                                <h6 class="card-subtitle mb-4 text-muted">$3.99/mo</h6>

                                <div class="form-check form-switch">
                                    <input 
                                        class="form-check-input" 
                                        type="checkbox" 
                                        id="gallery-1-item-1" 
                                        data-jso-compare-cb />

                                    <label 
                                        for="gallery-1-item-1"
                                        class="form-check-label text-decoration-underline">Compare</label>
                                </div>
                            </div>
                        </div>
                    </div>

                    <!-- Any number of gallery items here ... 
                         Please note that checkbox IDs 
                         and data-jso-item attributes should be unique.
                    -->

                </div>
            </div>

            <!-- right button -->
            <button 
                data-jso-right-btn
                type="button" 
                class="btn btn-dark position-absolute px-3 d-none d-lg-inline-block rounded-pill" 
                style="right: -3rem">&raquo;</button>

        </div>
    </div>
</div>

Please note the following points:

  • - The gallery container should have data-jso-gallery attribute.
  • - The left button should have data-jso-left-btn attribute.
  • - The right button should have data-jso-right-btn attribute.
  • - The gallery content container should have data-jso-gallery-holder attribute.
  • - Each gallery item should have data-jso-item attribute.
  • - Gallery checkboxes should have data-jso-compare-cb attributes and also unique IDs.


These attributes allow changing HTML layout and styles the way they match best for the current website.


You may find all data attributes here.


Now add the comparison part after the gallery:

<!-- comparison -->
<div class="row jso-hidden" data-jso-compare-table>

    <!-- features definition -->
    <div class="col-12 col-lg-3 mb-2" data-jso-features>

        <!-- control buttons -->
        <div class="card mb-2 jso-header">
            <div class="card-body">

                <!-- hide identical rows button -->
                <button 
                    type="button" 
                    class="btn btn-danger" 
                    data-jso-hide-identical>Hide Identical</button>

                <!-- show all rows button -->
                <button 
                    type="button" 
                    class="btn btn-danger jso-hidden" 
                    data-jso-show-all>Show All</button>

                <!-- reset button -->
                <button 
                    type="button" 
                    class="btn btn-outline-dark" 
                    title="Reset" 
                    data-jso-reset>Reset</button>
            </div>
        </div>

        <!-- features definitions -->
        <ul class="list-group d-none d-lg-block">

            <!-- row #1 -->
            <li class="list-group-item" data-jso-name="Name 1">Name 1</li>

            <!-- row #2 -->
            <li class="list-group-item" data-jso-name="Name 2">Name 2</li>

            <!-- any number of rows here ... -->
        </ul>
    </div>

    <!-- data content -->
    <div class="col">
        <div class="row">

            <!-- column #1 -->
            <div class="col-12 col-md-6 col-lg-4 mb-2" data-jso-item="1">

                <!-- column header -->
                <div class="card text-white bg-dark mb-2 jso-header">
                    <div class="card-body position-relative">

                    <!-- close button -->
                    <button 
                        data-jso-close
                        type="button" 
                        class="btn-close btn-close-white position-absolute top-0 end-0 m-1" 
                        aria-label="Close"></button>

                    <h5 class="card-title">Starter</h5>
                    <h6 class="card-subtitle text-muted">$3.99/mo</h6>
                    </div>
                </div>

                <!-- column data list -->
                <ul class="list-group">

                    <!-- row #1 -->
                    <li class="list-group-item" data-jso-name="Name 1">Value 1</li>

                    <!-- row #2 -->
                    <li class="list-group-item" data-jso-name="Name 2">Value 2</li>

                    <!-- any number of rows here... -->

                </ul>
            </div>

            <!-- any number of columns here ... -->

        </div> 
    </div>  

</div>

Please note the following points:

  • - The comparison part container should have data-jso-compare-table attribute.
  • - The features definitions container should have data-jso-features attribute.
  • - The hide identical rows button should have data-jso-hide-identical attribute.
  • - The 'Show all' button should have data-jso-show-all attribute.
  • - The reset button should have data-jso-reset attribute.
  • - Close buttons should have data-jso-close attribute.


You may find all data attributes here.


How to connect between gallery slide and appropriate column?

To connect between gallery slide and appropriate column, use data-jso-item attribute. For example, both gallery slide and its column may have data-jso-item="1".


How to connect between feature name and appropriate row?

To connect between feature name and appropriate row, use data-jso-name attribute. For example, feature name (located inside container with data-jso-features attribute) may be:

<li class="list-group-item" data-jso-name="Disk Space">Disk Space</li>

and then feature row located inside data-jso-item will be:

<li class="list-group-item" data-jso-name="Disk Space">10 GB</li>

In mobile, data-jso-name attribute will be used as an inline title inside each cell.


How initialize the script?

Place the <script> near the end of your pages, right before the closing </body> tag:

<script src="pcompare.min.js"></script>

Add library initialization inside the script tags:

window.addEventListener('DOMContentLoaded', function() {
    pCompare();
});

It's possible to pass the library settings in the following way:

window.addEventListener('DOMContentLoaded', function() {
    pCompare({
        // settings here...
    });
});

Add the following styles to the HEAD section:

<style>
/**
 * You may change this height depending on the website styles
 */
.jso-header{
    height: 80px;
}

.jso-hidden{
    display: none !important;
}

/**
 * mobile: comparison table titles
 */
@media (max-width: 992px) {
    [data-jso-name]::before{
       content: attr(data-jso-name);
       display: block;
       font-weight: 500;
    }
}
</style>

You may find the complete examples of each CSS Framework and pure CSS version in the /main/examples/ folder.


Tailwind Example

This tutorial will demonstrate how to implement the Product Compare Library with Tailwind CSS Framework.

Get started with a minimal Tailwind CSS layout as described in Tailwind CSS documentation. In this example, we will use the CDN approach for simplicity, but you can use another installation method.

<!doctype html>
<html>
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Demo Page</title>

  <!-- Tailwind CSS -->
  <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">

</head>
<body>


</body>
</html>

You can use any CSS Framework or your custom HTML & CSS.



Create a container that will hold all content. The container should have data-jso-pcompare attribute.

<div class="max-w-screen-xl mx-auto my-12 relative" data-jso-pcompare>

</div>

You may find other ways to define containers here.


Create a gallery layout inside the container:

<div class="max-w-screen-xl mx-auto my-12 relative" data-jso-pcompare>

    <!-- gallery -->
    <div class="flex items-center relative mb-4" data-jso-gallery>

        <button 
            data-jso-left-btn 
            type="button" 
            class="bg-gray-800 hover:bg-gray-700 text-white mr-4 absolute px-5 py-2 hidden lg:inline-block rounded" 
            style="left: -3rem">&laquo;</button>

        <div class="flex-grow overflow-hidden">
            <div class="flex flex-nowrap" data-jso-gallery-holder>

                <!-- gallery item #1 -->
                <div class="w-full md:w-6/12 lg:w-3/12 flex-grow-0 flex-shrink-0" data-jso-item="1">
                    <div class="bg-white mx-2 px-4 py-2 rounded">
                        <h5 class="font-bold text-lg">Slide Title</h5>

                        <!-- ... Slide HTML layout / styles can be changed ... -->

                        <label class="border border-blue-500 bg-white hover:bg-blue-500 text-blue-500 hover:text-white w-full block mb-4 px-4 py-3 cursor-pointer text-gray-100 text-center rounded">Compare
                            <input 
                                class="mr-2 hidden" 
                                type="checkbox" 
                                data-jso-compare-cb 
                            />
                        </label>
                    </div>
                </div>

                <!-- Any number of gallery items here ... 
                    Please note that checkbox IDs 
                    and data-jso-item attributes should be unique.
                -->

            </div>
        </div>

        <button 
            data-jso-right-btn
            type="button" 
            class="bg-gray-800 hover:bg-gray-700 text-white ml-4 absolute px-5 py-2 hidden lg:inline-block rounded" 
            style="right: -3rem">&raquo;</button>

    </div>

</div>

Please note the following points:

  • - The gallery container should have data-jso-gallery attribute.
  • - The left button should have data-jso-left-btn attribute.
  • - The right button should have data-jso-right-btn attribute.
  • - The gallery content container should have data-jso-gallery-holder attribute.
  • - Each gallery item should have data-jso-item attribute.
  • - Gallery checkboxes should have data-jso-compare-cb attributes and also unique IDs.


These attributes allow changing HTML layout and styles the way they match best for the current website.


You may find all data attributes here.


Now add the comparison part after the gallery:

<!-- comparison -->
<div class="flex flex-wrap lg:flex-nowrap jso-hidden" data-jso-compare-table>

    <!-- comparison features definition -->
    <div class="w-full lg:w-3/12 flex-grow-0 flex-shrink-0 mb-2" data-jso-features>

        <!-- comparison buttons -->
        <div class="bg-white mx-2 px-4 py-2 rounded mb-2 h-16 flex items-center">
            <div class="w-6/12 lg:w-8/12">
                <button type="button" class="bg-red-500 text-white w-full rounded truncate px-4 py-2" data-jso-hide-identical>Hide Identical</button>
                <button type="button" class="bg-red-500 text-white w-full rounded truncate px-4 py-2 jso-hidden" data-jso-show-all>Show All</button>
            </div>
            <div class="w-6/12 lg:w-4/12 flex">
                <button type="button" class="border border-gray-900 rounded  px-4 py-2 w-full ml-4 text-center flex justify-center" title="Reset" data-jso-reset>Reset</button>
            </div>
        </div>

        <!-- features definitions -->
        <ul class="bg-white mx-2 hidden lg:block rounded">

            <!-- row #1 -->
            <li class="px-4 py-2 border-b border-gray-100" data-jso-name="Name 1">Name 1</li>

            <!-- row #2 -->
            <li class="px-4 py-2 border-b border-gray-100" data-jso-name="Name 2">Name 2</li>

            <!-- any number of rows here ... -->
        </ul>
    </div>

    <!-- data columns -->
    <div class="flex flex-wrap w-full">

        <!-- column #1 -->
        <div class="w-full md:w-6/12 lg:w-4/12 flex-grow-0 flex-shrink-0 mb-2" data-jso-item="1">

            <!-- column header with a close button -->
            <div class="bg-gray-700 text-gray-100 mx-2 py-2 px-4 rounded mb-2 relative h-16">
                <button 
                    data-jso-close
                    type="button" 
                    class="absolute top-1 right-1 m-1" 
                    aria-label="Close">Close</button>

                <h5 class="font-bold">Starter</h5>
                <h6 class="text-gray-300">$3.99/mo</h6>
            </div>

            <!-- column data list -->
            <ul class="bg-white mx-2 rounded">

                <!-- row #1 -->
                <li class="px-4 py-2 border-b border-gray-100" data-jso-name="Name 1">Value 1</li>

                <!-- row #2 -->
                <li class="px-4 py-2 border-b border-gray-100" data-jso-name="Name 2">Value 2</li>

                <!-- any number of rows here... -->
            </ul>
        </div>

        <!-- any number of columns here ... --> 
    </div>  

</div>

Please note the following points:

  • - The comparison part container should have data-jso-compare-table attribute.
  • - The features definitions container should have data-jso-features attribute.
  • - The hide identical rows button should have data-jso-hide-identical attribute.
  • - The 'Show all' button should have data-jso-show-all attribute.
  • - The reset button should have data-jso-reset attribute.
  • - Close buttons should have data-jso-close attribute.


You may find all data attributes here.


How to connect between gallery slide and appropriate column?

To connect between gallery slide and appropriate column, use data-jso-item attribute. For example, both gallery slide and its column may have data-jso-item="1".


How to connect between feature name and appropriate row?

To connect between feature name and appropriate row, use data-jso-name attribute. For example, feature name (located inside container with data-jso-features attribute) may be:

<li data-jso-name="Disk Space">Disk Space</li>

and then feature row located inside data-jso-item will be:

<li data-jso-name="Disk Space">10 GB</li>

In mobile, data-jso-name attribute will be used as an inline title inside each cell.


How initialize the script?

Place the <script> near the end of your pages, right before the closing </body> tag:

<script src="pcompare.min.js"></script>

Add library initialization inside the script tags:

window.addEventListener('DOMContentLoaded', function() {
    pCompare();
});

It's possible to pass the library settings in the following way:

window.addEventListener('DOMContentLoaded', function() {
    pCompare({
        // settings here...
    });
});

Add the below styles to the HEAD section. The styles can be fine-tuned to match the look and feel of the current website.

<style>
.jso-hidden{
    display: none !important;
}

/**
 * mobile: comparison table titles
 */
@media (max-width: 992px) {
    [data-jso-name]::before{
       content: attr(data-jso-name);
       display: block;
       margin-right: auto;
    }
}

[data-jso-name]{
    height: 2.5rem;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-align: center;
        -ms-flex-align: center;
            align-items: center
}

[data-jso-left-btn]:disabled,
[data-jso-right-btn]:disabled{
    background-color: #ccc;
}

label{
    transition: 0.3s all;
}

.jso-disabled label,
.jso-disabled label:hover{
    background-color: #e4e4e4;
    color: #808080;
    border-color: #808080;
}

.jso-selected label{
    background-color: rgba(59, 130, 246);
    color: #fff;
}
</style>

You may find the complete examples of each CSS Framework and pure CSS version in the /main/examples/ folder.


Bulma Example

This tutorial will demonstrate how to implement the Product Compare Library with Bulma CSS Framework.

Get started with a minimal Bulma CSS layout as described in Bulma CSS documentation. In this example, we will use the CDN approach for simplicity, but you can use another installation method.

<!doctype html>
<html>
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Demo Page</title>

  <!-- Bulma CSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">

</head>
<body>


</body>
</html>

You can use any CSS Framework or your custom HTML & CSS.



Create a container that will hold all content. The container should have data-jso-pcompare attribute.

<div data-jso-pcompare class="container my-5">

</div>

You may find other ways to define containers here.


Create a gallery layout inside the container:

<!-- gallery -->
<div class="columns">
    <div class="column is-flex is-align-items-center mb-4 is-relative" data-jso-gallery style="max-width: 100%">

      <button type="button" style="left: -3rem" class="button is-hidden-mobile is-inline-block-desktop mr-2 px-4 is-rounded is-link is-absolute" data-jso-left-btn>«</button>

      <div class="is-clipped is-flex-grow-1">
         <div data-jso-gallery-holder class="columns is-mobile is-flex-wrap-nowrap">

            <!-- gallery item #1 -->
            <div data-jso-item="1" class="column is-12-mobile is-6-tablet is-3-desktop">
               <div class="has-background-white p-4">
                  <h5 class="has-text-weight-bold is-size-5">Starter</h5>
                    <h6 class="mb-4 has-text-grey">$3.99/mo</h6>

                    <div>
                        <input type="checkbox" id="gallery-1-item-1" data-jso-compare-cb>
                        <label for="gallery-1-item-1">Compare</label>
                    </div>
                </div>
            </div>

            <!-- Any number of gallery items here ... 
                Please note that checkbox IDs 
                and data-jso-item attributes should be unique.
            -->

         </div>
      </div>

      <button type="button" style="right: -3rem" class="button is-hidden-mobile is-inline-block-desktop ml-2 px-4 is-rounded is-link is-absolute" data-jso-right-btn>»</button>

    </div>
</div>

Please note the following points:

  • - The gallery container should have data-jso-gallery attribute.
  • - The left button should have data-jso-left-btn attribute.
  • - The right button should have data-jso-right-btn attribute.
  • - The gallery content container should have data-jso-gallery-holder attribute.
  • - Each gallery item should have data-jso-item attribute.
  • - Gallery checkboxes should have data-jso-compare-cb attributes and also unique IDs.


These attributes allow changing HTML layout and styles the way they match best for the current website.


You may find all data attributes here.


Now add the comparison part after the gallery:

<!-- comparison -->
<div class="columns jso-hidden" data-jso-compare-table>

    <!-- comparison features / fields definition -->
    <div class="column is-12-mobile is-3-desktop mb-2" data-jso-features>

        <!-- control buttons -->
        <div class="columns is-mobile has-background-white p-4 mb-2 mx-0 is-align-items-center jso-header">

            <div class="column is-6 is-8-desktop">
                <button type="button" class="button is-danger" data-jso-hide-identical>Hide Identical</button>
                <button type="button" class="button is-danger jso-hidden" data-jso-show-all>Show All</button>
            </div>

            <div class="column is-6 is-4-desktop">
                <button type="button" title="Reset" class="button" data-jso-reset>Reset</button>
            </div>

        </div>

        <!-- fields -->
        <ul class="box is-hidden-mobile is-block-desktop">

            <!-- row #1 -->
            <li class="my-1" data-jso-name="Name 1">Name 1</li>

            <!-- row #2 -->
            <li class="my-1" data-jso-name="Name 2">Name 2</li>

            <!-- any number of rows here ... -->
        </ul>
    </div>

    <!-- data columns -->
    <div class="column pt-0">
        <div class="columns pt-0">

            <!-- column #1 -->
            <div data-jso-item="1" class="column is-12-mobile is-6-tablet is-4-desktop mb-2">

                <!-- column header with a close button -->
                <div class="has-text-white has-background-dark mb-2 px-4 is-relative is-flex is-flex-direction-column is-justify-content-center jso-header">
                    <button type="button" class="button close-button has-text-white" aria-label="Close" data-jso-close>Close</button>
                    <h5>Starter</h5>
                    <h6 class="has-text-grey">$3.99/mo</h6>
                </div>

                <!-- column data list -->
                <ul class="box">

                    <!-- row #1 -->
                    <li class="my-1" data-jso-name="Name 1">Value 1</li>

                    <!-- row #2 -->
                    <li class="my-1" data-jso-name="Name 2">Value 2</li>

                    <!-- any number of rows here... -->
                </ul>
            </div>

            <!-- any number of columns here ... --> 

        </div> 
    </div>  

</div>

Please note the following points:

  • - The comparison part container should have data-jso-compare-table attribute.
  • - The features definitions container should have data-jso-features attribute.
  • - The hide identical rows button should have data-jso-hide-identical attribute.
  • - The 'Show all' button should have data-jso-show-all attribute.
  • - The reset button should have data-jso-reset attribute.
  • - Close buttons should have data-jso-close attribute.


You may find all data attributes here.


How to connect between gallery slide and appropriate column?

To connect between gallery slide and appropriate column, use data-jso-item attribute. For example, both gallery slide and its column may have data-jso-item="1".


How to connect between feature name and appropriate row?

To connect between feature name and appropriate row, use data-jso-name attribute. For example, feature name (located inside container with data-jso-features attribute) may be:

<li data-jso-name="Disk Space">Disk Space</li>

and then feature row located inside data-jso-item will be:

<li data-jso-name="Disk Space">10 GB</li>

In mobile, data-jso-name attribute will be used as an inline title inside each cell.


How initialize the script?

Place the <script> near the end of your pages, right before the closing </body> tag:

<script src="pcompare.min.js"></script>

Add library initialization inside the script tags:

window.addEventListener('DOMContentLoaded', function() {
    pCompare();
});

It's possible to pass the library settings in the following way:

window.addEventListener('DOMContentLoaded', function() {
    pCompare({
        // settings here...
    });
});

Add the below styles to the HEAD section. The styles can be fine-tuned to match the look and feel of the current website.

<style>
.jso-header{
   height: 80px;
}

.jso-hidden{
   display: none !important;
}

.is-absolute{
   position: absolute;
}

.close-button{
   position: absolute;
   right: 0.5rem;
   top: 0.5rem;
   background: transparent;
}

[data-jso-hide-identical],
[data-jso-show-all],
[data-jso-reset]{
   width: 100%;
}

/**
 * mobile: comparison table titles
 */
@media (max-width: 992px) {
   [data-jso-name]::before{
      content: attr(data-jso-name);
      display: block;
      font-weight: 500;
   }
}

@media (max-width: 1500px) {
   [data-jso-left-btn],
   [data-jso-right-btn]{
      display: none !important;
   }
}
</style>

You may find the complete examples of each CSS Framework and pure CSS version in the /main/examples/ folder.


UIkit Example

This tutorial will demonstrate how to implement the Product Compare Library with UIkit CSS Framework.

Get started with a minimal UIkit CSS layout as described in UIkit CSS documentation. In this example, we will use the CDN approach for simplicity, but you can use another installation method.

<!doctype html>
<html>
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Demo Page</title>

  <!-- UIkit CSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.7.3/dist/css/uikit.min.css">

</head>
<body>


</body>
</html>

You can use any CSS Framework or your custom HTML & CSS.



Create a container that will hold all content. The container should have data-jso-pcompare attribute.

<section class="uk-background-muted uk-padding-large uk-position-relative">
    <div data-jso-pcompare class="uk-container-expand uk-margin-medium-top uk-margin-medium-bottom uk-position-relative">

    </div>
</section>

You may find other ways to define containers here.


Create a gallery layout inside the container:

<!-- gallery -->
<div class="uk-flex uk-margin-medium-bottom">
    <div data-jso-gallery class="uk-width-1-1 uk-flex uk-flex-middle uk-position-relative">

        <!-- left button -->
        <button data-jso-left-btn type="button" style="left: -3rem"
            class="uk-button uk-margin-small-right uk-position-absolute uk-padding-medium-left uk-padding-medium-right uk-hidden uk-display-inline-block@l uk-border-pill uk-position-z-index">«</button>

        <div class="uk-flex-1 uk-overflow-hidden">
            <div data-jso-gallery-holder class="uk-flex uk-flex-nowrap">

                <!-- gallery item #1 -->
                <div data-jso-item="1" class="uk-width-1-1 uk-width-1-2@m uk-width-1-4@l">
                    <div class="uk-card uk-card-body uk-card-default uk-margin-small-left uk-margin-small-right">
                        <h5 class="uk-card-title uk-margin-small">Starter</h5>
                        <h6 class="uk-text-muted uk-margin-small">$3.99/mo</h6>

                        <div>
                            <input type="checkbox" id="gallery-1-item-1" data-jso-compare-cb>
                            <label for="gallery-1-item-1">Compare</label>
                        </div>
                    </div>
                </div>

                <!-- Any number of gallery items here ... 
                    Please note that checkbox IDs 
                    and data-jso-item attributes should be unique.
                -->

            </div>
        </div>

        <!-- right button -->
        <button data-jso-right-btn type="button" style="right: -3rem"
            class="uk-button uk-margin-small-left uk-position-absolute uk-padding-medium-left uk-padding-medium-right uk-hidden uk-display-inline-block@l uk-border-pill uk-position-z-index">»</button>

    </div>
</div>

Please note the following points:

  • - The gallery container should have data-jso-gallery attribute.
  • - The left button should have data-jso-left-btn attribute.
  • - The right button should have data-jso-right-btn attribute.
  • - The gallery content container should have data-jso-gallery-holder attribute.
  • - Each gallery item should have data-jso-item attribute.
  • - Gallery checkboxes should have data-jso-compare-cb attributes and also unique IDs.


These attributes allow changing HTML layout and styles the way they match best for the current website.


You may find all data attributes here.


Now add the comparison part after the gallery:

<!-- comparison -->
<div data-jso-compare-table class="uk-flex uk-flex-wrap jso-hidden">

    <!-- comparison features / fields definition -->
    <div data-jso-features class="uk-width-1-1 uk-width-1-4@l">

        <!-- control buttons -->
        <div class="uk-card uk-card-body uk-card-default uk-margin-small-left uk-margin-small-right jso-header">
            <div class="uk-flex uk-flex-column">

                <div>
                    <button type="button" data-jso-hide-identical class="uk-button uk-button-danger uk-text-truncate">Hide Identical</button>
                    <button type="button" data-jso-show-all class="uk-button uk-button-danger uk-text-truncate jso-hidden">Show All</button>
                </div>

                <div>
                    <button type="button" title="Reset" data-jso-reset class="uk-button">Reset</button>
                </div>
            </div>
        </div>

        <!-- fields -->
        <ul class="uk-list uk-list-divider uk-hidden uk-display-block@l uk-margin-small-left uk-margin-small-right">

            <!-- row #1 -->
            <li data-jso-name="Name 1">Name 1</li>

            <!-- row #2 -->
            <li data-jso-name="Name 2">Name 2</li>

            <!-- any number of rows here ... -->
        </ul>
    </div>

    <!-- data columns -->
    <div class="uk-width-1-1 uk-width-3-4@l">
        <div class="uk-flex uk-flex-wrap">

            <!-- column #1 -->
            <div data-jso-item="1" class="uk-width-1-1 uk-width-1-2@m uk-width-1-3@l">

                <!-- column header with a close button -->
                <div class="uk-card uk-card-body uk-card-secondary uk-position-relative uk-margin-small-left uk-margin-small-right jso-header">
                    <button type="button" uk-close data-jso-close aria-label="Close" class="uk-button uk-button-danger uk-position-absolute"></button>
                    <h5 class="uk-card-title uk-margin-small">Starter</h5>
                    <h6 class="uk-text-muted uk-margin-small">$3.99/mo</h6>
                </div>

                <!-- column data list -->
                <ul class="uk-list uk-list-divider uk-margin-small-left uk-margin-small-right">

                    <!-- row #1 -->
                    <li data-jso-name="Name 1">Value 1</li>

                    <!-- row #2 -->
                    <li data-jso-name="Name 2">Value 2</li>

                    <!-- any number of rows here... -->
                </ul>
            </div>

            <!-- any number of columns here ... --> 

        </div>
    </div>

</div>

Please note the following points:

  • - The comparison part container should have data-jso-compare-table attribute.
  • - The features definitions container should have data-jso-features attribute.
  • - The hide identical rows button should have data-jso-hide-identical attribute.
  • - The 'Show all' button should have data-jso-show-all attribute.
  • - The reset button should have data-jso-reset attribute.
  • - Close buttons should have data-jso-close attribute.


You may find all data attributes here.


How to connect between gallery slide and appropriate column?

To connect between gallery slide and appropriate column, use data-jso-item attribute. For example, both gallery slide and its column may have data-jso-item="1".


How to connect between feature name and appropriate row?

To connect between feature name and appropriate row, use data-jso-name attribute. For example, feature name (located inside container with data-jso-features attribute) may be:

<li data-jso-name="Disk Space">Disk Space</li>

and then feature row located inside data-jso-item will be:

<li data-jso-name="Disk Space">10 GB</li>

In mobile, data-jso-name attribute will be used as an inline title inside each cell.


How initialize the script?

Place the <script> near the end of your pages, right before the closing </body> tag:

<script src="pcompare.min.js"></script>

Add library initialization inside the script tags:

window.addEventListener('DOMContentLoaded', function() {
    pCompare();
});

It's possible to pass the library settings in the following way:

window.addEventListener('DOMContentLoaded', function() {
    pCompare({
        // settings here...
    });
});

Add the below styles to the HEAD section. The styles can be fine-tuned to match the look and feel of the current website.

<style>
.jso-header {
    height: 140px;
}

.jso-hidden {
    display: none !important;
}

[data-jso-item] {
    -ms-flex-negative: 0;
    flex-shrink: 0;
}

[data-jso-close],
.uk-label{
    top: 0.5rem;
    right: 0.5rem;
}

[data-jso-hide-identical],
[data-jso-show-all],
[data-jso-reset]{
    width: 100%;
}

[data-jso-reset]{
    margin-top: 3px;
}

@media (min-width: 960px){
    .uk-display-inline-block\@m{
        display: inline-block !important;
    }

    .uk-display-block\@m{
        display: block !important;
    }

    .uk-flex-row\@m{
        -webkit-box-orient: horizontal;
        -webkit-box-direction: normal;
            -ms-flex-direction: row;
                flex-direction: row;
    }
}

@media (min-width: 1200px){
    .uk-display-inline-block\@l{
        display: inline-block !important;
    }

    .uk-display-block\@l{
        display: block !important;
    }
}

/**
 * mobile: comparison table titles
 */
@media (max-width: 992px) {
    [data-jso-name]::before {
        content: attr(data-jso-name);
        display: inline;
        font-weight: 500;
        position: static;
        width: auto;
        margin: 0;
        left: 0;
        height: auto;
        text-align: left;
    }

    .uk-list [data-jso-name]{
        display: -webkit-box;
        display: -ms-flexbox;
        display: flex;
        -webkit-box-pack: justify;
            -ms-flex-pack: justify;
                justify-content: space-between;
    }

    .uk-heading-medium{
        font-size: 2.4rem;
    }

    .uk-heading-small{
        font-size: 1.8rem;
    }
}
</style>

You may find the complete examples of each CSS Framework and pure CSS version in the /main/examples/ folder.


Foundation Example

This tutorial will demonstrate how to implement the Product Compare Library with Foundation CSS Framework.

Get started with a minimal Foundation CSS layout as described in Foundation CSS documentation. In this example, we will use the CDN approach for simplicity, but you can use another installation method.

<!doctype html>
<html>
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Demo Page</title>

  <!-- Foundation CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/6.7.1/css/foundation-prototype.min.css" integrity="sha512-qcpJaaaCw4xq7tKqYGOGNmXlvjfhhqOcfemIyX4OXiBPqtwJxqKAks/U+oVc0ArqFLjlDGj9NQaWf9NBJU1Fmw==" crossorigin="anonymous" referrerpolicy="no-referrer">

</head>
<body>


</body>
</html>

You can use any CSS Framework or your custom HTML & CSS.



Create a container that will hold all content. The container should have data-jso-pcompare attribute.

<div data-jso-pcompare class="grid-container position-relative">

</div>

You may find other ways to define containers here.


Create a gallery layout inside the container:

<!-- gallery -->
<div class="grid-x">
    <div data-jso-gallery class="auto cell flex-container align-middle position-relative">

        <!-- left button -->
        <button data-jso-left-btn type="button" style="left: -7rem" class="button margin-right-2 position-absolute padding-horizontal-3 hide large-display-inline-block">«</button>

        <div class="flex-grow-1 overflow-hidden">
            <div data-jso-gallery-holder class="grid-x flex-nowrap">

                <!-- gallery item #1 -->
                <div data-jso-item="1" class="small-12 cell medium-6 large-3">
                    <div class="margin-horizontal-1">
                        <div class="card">
                            <div class="card-section">
                                <h5 class="font-bold">Starter</h5>
                                <h6 class="subheader">$3.99/mo</h6>

                                <div>
                                    <input type="checkbox" id="gallery-1-item-1" data-jso-compare-cb checked>
                                    <label for="gallery-1-item-1" class="is-underlined">Compare</label>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

                <!-- Any number of gallery items here ... 
                    Please note that checkbox IDs 
                    and data-jso-item attributes should be unique.
                -->

            </div>
        </div>

        <!-- right button -->
        <button data-jso-right-btn type="button" style="right: -7rem" class="button margin-left-2 position-absolute padding-horizontal-3 hide large-display-inline-block">»</button>

    </div>
</div>

Please note the following points:

  • - The gallery container should have data-jso-gallery attribute.
  • - The left button should have data-jso-left-btn attribute.
  • - The right button should have data-jso-right-btn attribute.
  • - The gallery content container should have data-jso-gallery-holder attribute.
  • - Each gallery item should have data-jso-item attribute.
  • - Gallery checkboxes should have data-jso-compare-cb attributes and also unique IDs.


These attributes allow changing HTML layout and styles the way they match best for the current website.


You may find all data attributes here.


Now add the comparison part after the gallery:

<!-- comparison -->
<div data-jso-compare-table class="grid-x flex-dir-column large-flex-dir-row jso-hidden">

    <!-- comparison features / fields definition -->
    <div data-jso-features class="small-12 cell large-3">

        <!-- control buttons -->
        <div class="margin-horizontal-1 margin-bottom-2">
            <div class="card jso-header">
                <div class="card-section flex-container align-justify align-middle">
                    <button type="button" data-jso-hide-identical class="button alert margin-bottom-0 margin-right-1">Hide Identical</button>
                    <button type="button" data-jso-show-all class="button alert margin-bottom-0 margin-right-1 jso-hidden">Show All</button>
                    <button type="button" title="Reset" data-jso-reset class="button">Reset</button>
                </div>
            </div>
        </div>

        <!-- fields -->
        <ul class="hide large-display-block list-group margin-horizontal-1">

            <!-- row #1 -->
            <li data-jso-name="Name 1">Name 1</li>

            <!-- row #2 -->
            <li data-jso-name="Name 2">Name 2</li>

            <!-- any number of rows here ... -->
        </ul>
    </div>

    <!-- data columns -->
    <div class="auto cell">
        <div class="grid-x">

            <!-- column #1 -->
            <div data-jso-item="1" class="small-12 cell medium-6 large-4 margin-bottom-2">

                <!-- column header with a close button -->
                <div class="margin-horizontal-1">
                    <div class="card callout secondary border-none margin-bottom-2 jso-header position-relative">
                        <button data-jso-close type="button" aria-label="Close" class="position-absolute">Close</button>
                        <h5>Starter</h5>
                        <h6>$3.99/mo</h6>
                    </div>
                </div>

                <!-- column data list -->
                <ul class="list-group margin-horizontal-1">

                    <!-- row #1 -->
                    <li data-jso-name="Name 1">Value 1</li>

                    <!-- row #2 -->
                    <li data-jso-name="Name 2">Value 2</li>

                    <!-- any number of rows here... -->
                </ul>
            </div>

            <!-- any number of columns here ... --> 

        </div> 
    </div>  

</div>

Please note the following points:

  • - The comparison part container should have data-jso-compare-table attribute.
  • - The features definitions container should have data-jso-features attribute.
  • - The hide identical rows button should have data-jso-hide-identical attribute.
  • - The 'Show all' button should have data-jso-show-all attribute.
  • - The reset button should have data-jso-reset attribute.
  • - Close buttons should have data-jso-close attribute.


You may find all data attributes here.


How to connect between gallery slide and appropriate column?

To connect between gallery slide and appropriate column, use data-jso-item attribute. For example, both gallery slide and its column may have data-jso-item="1".


How to connect between feature name and appropriate row?

To connect between feature name and appropriate row, use data-jso-name attribute. For example, feature name (located inside container with data-jso-features attribute) may be:

<li data-jso-name="Disk Space">Disk Space</li>

and then feature row located inside data-jso-item will be:

<li data-jso-name="Disk Space">10 GB</li>

In mobile, data-jso-name attribute will be used as an inline title inside each cell.


How initialize the script?

Place the <script> near the end of your pages, right before the closing </body> tag:

<script src="pcompare.min.js"></script>

Add library initialization inside the script tags:

window.addEventListener('DOMContentLoaded', function() {
    pCompare();
});

It's possible to pass the library settings in the following way:

window.addEventListener('DOMContentLoaded', function() {
    pCompare({
        // settings here...
    });
});

Add the below styles to the HEAD section. The styles can be fine-tuned to match the look and feel of the current website.

<style>
.jso-header{
    height: 95px;
}

.jso-hidden{
    display: none !important;
}

/**
 * mobile: comparison table titles
 */
@media (max-width: 992px) {
    [data-jso-name]::before{
       content: attr(data-jso-name);
       display: block;
       font-weight: 500;
    }
} 

.flex-dir-column{
    -webkit-box-orient: vertical;
    -webkit-box-direction: normal;
        -ms-flex-direction: column;
            flex-direction: column;
}

@media (min-width: 640px) {
    .medium-display-inline-block{
        display: inline-block !important;
    }

    .medium-display-block{
        display: block !important;
    }

    .medium-flex-dir-row{
        -webkit-box-orient: horizontal;
        -webkit-box-direction: normal;
            -ms-flex-direction: row;
                flex-direction: row;
    }
}

@media (min-width: 1024px) {
    .large-display-inline-block{
        display: inline-block !important;
    }

    .large-display-block{
        display: block !important;
    }

    .large-flex-dir-row{
        -webkit-box-orient: horizontal;
        -webkit-box-direction: normal;
            -ms-flex-direction: row;
                flex-direction: row;
    }
}

.menu a{
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
}

.margin-horizontal-auto{
    margin: auto;
}

.flex-grow-1{
    -webkit-box-flex: 1;
        -ms-flex-positive: 1;
            flex-grow: 1;
}

.flex-nowrap{
    -ms-flex-wrap: nowrap;
        flex-wrap: nowrap;
}

.jso-badge,
[data-jso-close]{
    top: 0.5rem;
    right: 0.5rem;
    font-size: 1rem;
    font-weight: normal;
}

[data-jso-reset]{
    padding: 0.5rem 1rem;
}

.list-group{
    margin-bottom: 1rem;
    border-top: 1px solid #e6e6e6;
    border-left: 1px solid #e6e6e6;
    border-right: 1px solid #e6e6e6;
    border-radius: 0;
    background: #fefefe;
    -webkit-box-shadow: none;
            box-shadow: none;
    overflow: hidden;
    color: #0a0a0a;
}

.list-group li{
    padding: 0.5rem 1rem;
    border-bottom: 1px solid #e6e6e6;
}

[data-jso-compare-table] .cell.auto{
    flex: 1;
}
</style>

You may find the complete examples of each CSS Framework and pure CSS version in the /main/examples/ folder.


CSS Example (without frameworks)

This tutorial will demonstrate how to implement the Product Compare Library without CSS Frameworks.

Get started with a minimal HTML layout:

<!doctype html>
<html>
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Demo Page</title>


</head>
<body>


</body>
</html>

You can use any CSS Framework or your custom HTML & CSS.



Create a container that will hold all content. The container should have data-jso-pcompare attribute.

<div data-jso-pcompare>

</div>

You may find other ways to define containers here.


Create a gallery layout inside the container:

<!-- gallery -->
<div class="gallery" data-jso-gallery>

    <!-- left buton -->
    <button data-jso-left-btn type="button">&laquo;</button>

    <div class="gallery__content">
        <div data-jso-gallery-holder>

            <!-- gallery item #1 -->
            <div data-jso-item="1">
                <div class="gallery__item">
                    <h5>Starter</h5>
                    <h6>$3.99/mo</h6>

                    <div class="form-check form-switch">
                        <input class="form-check-input" type="checkbox" id="gallery-1-item-1" data-jso-compare-cb>
                        <label class="form-check-label text-decoration-underline" for="gallery-1-item-1">Compare</label>
                    </div>
                </div>
            </div>

            <!-- Any number of gallery items here ... 
                Please note that checkbox IDs 
                and data-jso-item attributes should be unique.
            -->

        </div>
    </div>

    <!-- right buton -->
    <button  data-jso-right-btn type="button">&raquo;</button>
</div>

Please note the following points:

  • - The gallery container should have data-jso-gallery attribute.
  • - The left button should have data-jso-left-btn attribute.
  • - The right button should have data-jso-right-btn attribute.
  • - The gallery content container should have data-jso-gallery-holder attribute.
  • - Each gallery item should have data-jso-item attribute.
  • - Gallery checkboxes should have data-jso-compare-cb attributes and also unique IDs.


These attributes allow changing HTML layout and styles the way they match best for the current website.


You may find all data attributes here.


Now add the comparison part after the gallery:

<!-- comparison -->
<div class="jso-hidden" data-jso-compare-table>

    <!-- features definition -->
    <div data-jso-features>

        <!-- control buttons -->
        <div class="compare_buttons">
            <button type="button" data-jso-hide-identical>Hide Identical</button>
            <button type="button" class="jso-hidden" data-jso-show-all>Show All</button>
            <button type="button" title="Reset" data-jso-reset>Reset</button>
        </div>

        <!-- fields -->
        <ul class="list-group">

            <!-- row #1 -->
            <li class="list-group-item" data-jso-name="Name 1">Name 1</li>

            <!-- row #2 -->
            <li class="list-group-item" data-jso-name="Name 2">Name 2</li>

            <!-- any number of rows here ... -->
        </ul>
    </div>

    <div class="compare__content">

        <!-- column #1 -->
        <div data-jso-item="1">
            <div class="compare__item">

                <!-- column header with a close button -->
                <div class="jso-header">
                    <button data-jso-close type="button" aria-label="Close">Close</button>
                    <h5>Starter</h5>
                    <h6>$3.99/mo</h6>
                </div>

                <!-- column data list -->
                <ul class="list-group">

                    <!-- row #1 -->
                    <li class="list-group-item" data-jso-name="Name 1">Value 1</li>

                    <!-- row #2 -->
                    <li class="list-group-item" data-jso-name="Name 2">Value 2</li>

                    <!-- any number of rows here... -->
                </ul>
            </div>
        </div>

        <!-- any number of columns here ... --> 

        </div>  
    </div>  

</div>

Please note the following points:

  • - The comparison part container should have data-jso-compare-table attribute.
  • - The features definitions container should have data-jso-features attribute.
  • - The hide identical rows button should have data-jso-hide-identical attribute.
  • - The 'Show all' button should have data-jso-show-all attribute.
  • - The reset button should have data-jso-reset attribute.
  • - Close buttons should have data-jso-close attribute.


You may find all data attributes here.


How to connect between gallery slide and appropriate column?

To connect between gallery slide and appropriate column, use data-jso-item attribute. For example, both gallery slide and its column may have data-jso-item="1".


How to connect between feature name and appropriate row?

To connect between feature name and appropriate row, use data-jso-name attribute. For example, feature name (located inside container with data-jso-features attribute) may be:

<li data-jso-name="Disk Space">Disk Space</li>

and then feature row located inside data-jso-item will be:

<li data-jso-name="Disk Space">10 GB</li>

In mobile, data-jso-name attribute will be used as an inline title inside each cell.


How initialize the script?

Place the <script> near the end of your pages, right before the closing </body> tag:

<script src="pcompare.min.js"></script>

Add library initialization inside the script tags:

window.addEventListener('DOMContentLoaded', function() {
    pCompare();
});

It's possible to pass the library settings in the following way:

window.addEventListener('DOMContentLoaded', function() {
    pCompare({
        // settings here...
    });
});

Add the below styles to the HEAD section. The styles can be fine-tuned to match the look and feel of the current website.

<style>
.jso-hidden{
    display: none;
}

.sub-title{
    font-weight: 300;
    font-size: 1.5rem;
    margin: 0 0 3rem 0;
    text-align: center;
    color: #797979;
}

.section{
    padding: 3rem 1rem;
    position: relative;
}

.section__light{
    background-color: #fbfbfb;
}

.section__dark{
    background-color: #111;
    color: #fff;
}

.section__white{
    background-color: #fff;
}

/**
 * section header
 */ 
.section__header{
    text-align: center;
    width: 550px;
    max-width: 100%;
    margin: auto;
}

.section__title{
    font-weight: 300;
    font-size: 1.8rem;
    padding: 0;
    margin: 0 0 0.5rem 0;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: center;
        -ms-flex-pack: center;
            justify-content: center;
    -webkit-box-align: center;
        -ms-flex-align: center;
            align-items: center;
}

.section__subtitle{
    font-weight: 300;
    font-size: 1.3rem;
    padding: 0;
    margin: 0;
    color: #828282;
}

.section__badge{
    background-color: aquamarine;
    padding: 0.5rem 1rem;
    font-size: 1rem;
    border-radius: 50%;
    margin-left: 1rem;
    color: #111;
}

/**
 * buttons
 */ 
.button{
    border: 0;
    padding: 1rem 2rem;
    border-radius: 0.5rem;
    margin: 0 0.5rem;
    font-size: 1rem;
    text-align: center;
    background: #111;
    color: #fff;
    -webkit-transition: 0.3s background-color;
    -o-transition: 0.3s background-color;
    transition: 0.3s background-color;
    cursor: pointer;
}

.button svg{
    margin-right: 0.5rem;
}

.button__outline{
    background: transparent;
    border: 1px solid #111;
    color: #111;
}

.button:hover{
    background: rgb(57, 57, 57);
}

.button__outline:hover{
    background: #fff;
}

.button:disabled {
    background-color: #ccc;
}

/**
 * compare container
 */
[data-jso-pcompare]{
    position: relative;
    padding: 3rem 0;
    width: 1200px;
    max-width: 100%;
    margin: auto;
}

/**
 * gallery
 */
.gallery__title{
    font-weight: bold;
    text-transform: uppercase;
    margin: 0 0 0.5rem 1rem;
}

.gallery{
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    position: relative;
    -webkit-box-align: center;
        -ms-flex-align: center;
            align-items: center;
    margin: 0 0 1rem 0;
}

.gallery__content{
    -webkit-box-flex: 1;
        -ms-flex-positive: 1;
            flex-grow: 1;
    overflow: hidden;
    position: relative;
}

[data-jso-gallery-holder]{
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -ms-flex-wrap: nowrap;
        flex-wrap: nowrap;
}

[data-jso-gallery-holder] [data-jso-item]{
    -webkit-box-flex: 0;
        -ms-flex: 0 0 auto;
            flex: 0 0 auto;
    width: 100%;
}

@media (min-width: 750px){
    [data-jso-gallery-holder] [data-jso-item]{
        width: 50%;
    }
}

@media (min-width: 1350px){
    [data-jso-gallery-holder] [data-jso-item]{
        width: 25%;
    }
}

.gallery__item{
    margin: 0 1rem;
    background-color: #fff;
    border: 1px solid #eee;
    border-radius: 0.5rem;
    padding: 1rem;
}

.gallery__item h5{
    font-weight: 300;
    font-size: 1.3rem;
    margin: 0;
    padding: 0;
}

.gallery__item h6{
    font-weight: normal;
    font-size: 1rem;
    color: #888;
    margin: 0;
    padding: 0 0 0.5rem 0;
}

.gallery__item-badge{
    position: absolute;
    background-color: #eddaff;
    padding: 0.5rem 1rem;
    font-size: 0.8rem;
    border-radius: 50%;
    margin-left: 1rem;
    color: #111;
}

[data-jso-left-btn],
[data-jso-right-btn]{
    position: absolute;
    background-color: #111;
    color: #fff;
    text-align: center;
    padding: 0.5rem 1rem;
    border-radius: 0.5rem;
    -webkit-appearance: none;
       -moz-appearance: none;
            appearance: none;
    border: 0;
    font-size: 1rem;
    display: none;
}

@media (min-width: 1350px){
    [data-jso-left-btn],
    [data-jso-right-btn]{
        display: inline-block;
    }
}

[data-jso-left-btn]{
    left: -3rem;
}

[data-jso-right-btn]{
    right: -3rem;
}

[data-jso-left-btn]:disabled,
[data-jso-right-btn]:disabled{
    background-color: #ccc;
}

/**
 * comparison table
 */
[data-jso-compare-table]{
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -ms-flex-wrap: wrap;
        flex-wrap: wrap;
}

[data-jso-features]{
    width: 100%;
}

@media (min-width: 750px) {
    [data-jso-features]{
        width: 50%;
    }  
}

@media (min-width: 1350px) {
    [data-jso-features]{
        width: 25%;
    }  
}

.compare__content{
    width: 100%;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -ms-flex-wrap: wrap;
        flex-wrap: wrap;
}

.compare__content [data-jso-item]{
    width: 100%;
    margin-bottom: 1rem;
}

@media (min-width: 750px){
    .compare__content [data-jso-item]{
        width: 100%;
    }  

    .compare__content{
        width: 50%;
    }
}

@media (min-width: 1350px){
    .compare__content [data-jso-item]{
        width: 33.33%;
    }  

    .compare__content{
        width: 75%;
    }
}

.compare__content .compare__item{
    margin: 0 1rem;
    background-color: #fff;
    border: 1px solid #eee;
    border-radius: 0.5rem;
}

.compare__content [data-jso-item] h5{
    font-weight: 300;
    font-size: 1.3rem;
    margin: 0;
    padding: 0;
}

.compare__content [data-jso-item] h6{
    font-weight: normal;
    font-size: 1rem;
    color: #888;
    margin: 0;
    padding: 0;
}

.compare_buttons{
    margin: 0 1rem;
    background-color: #fff;
    border: 1px solid #eee;
    border-radius: 0.5rem;
    padding: 1rem;
    height: 50px;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-align: center;
        -ms-flex-align: center;
            align-items: center;
}

.list-group{
    list-style: none;
    padding: 0;
    margin: 1rem;
}

.list-group-item{
    border-top: 1px solid #eee;
    padding: 0.5rem 1rem;
    display: -webkit-box;
    display: -ms-flexbox;
    display: flex;
    -webkit-box-pack: justify;
        -ms-flex-pack: justify;
            justify-content: space-between;
}

.list-group-item:first-child{
    border: 0;
}

.jso-header{
    height: 50px;
    position: relative;
    padding: 1rem;
    background: #383838;
    color: #fff;
    border-radius: 0.5rem 0.5rem 0 0;
}

[data-jso-close]{
    position: absolute;
    right: 0.5rem;
    top: 0.5rem;
    background: transparent;
    -webkit-appearance: none;
       -moz-appearance: none;
            appearance: none;
    border: 0;
    color: #fff;
    padding: 0;
    cursor: pointer;
}

[data-jso-hide-identical],
[data-jso-show-all]{
    -webkit-appearance: none;
       -moz-appearance: none;
            appearance: none;
    border: 0;
    background-color: #f5a0a0;
    padding: 0.7rem 1rem;
    font-size: 1rem;
    border-radius: 0.2rem;
    -webkit-box-flex: 1;
        -ms-flex: 1;
            flex: 1;
}

[data-jso-hide-identical]:disabled,
[data-jso-show-all]:disabled{
    background-color: #ffe9e9;
    color: #bdbdbd;
}

[data-jso-reset]{
    -webkit-appearance: none;
       -moz-appearance: none;
            appearance: none;
    border: 0;
    background-color: #fff;
    padding: 0.5rem 1rem;
}

/**
 * mobile: comparison table titles
 */
 @media (max-width: 749px) {
    [data-jso-name]::before {
        content: attr(data-jso-name);
    }
}
</style>

You may find the complete examples of each CSS Framework and pure CSS version in the /main/examples/ folder.


Data

Data Types

The library can compare the following data types using data-jso-type data attribute:

  • - texts (default)
  • - HTML elements
  • - numbers


If no attribute is defined, the rows will be compared as strings of text.


HTML data type can be declared in the following way:

<li data-jso-name="Field Name" data-jso-type="html">Field Name</li>

This data type can be used when HTML elements should be compared instead of the plain text—for example, icons, images, or complex HTML elements.


Number data type can be declared in the following way:

<li data-jso-name="Field Name" data-jso-type="number">Field Name</li>

If the number data type is defined, all characters except numbers will be ignored.


By default, the comparison is not case-sensitive. To enable case sensitivity to the specific field, add the data-jso-case-sensitive data attribute. This attribute can be applied only to text fields. This attribute also can be applied only to text fields.

<li data-jso-name="Field Name" data-jso-case-sensitive>Field Name</li>

It's possible to ignore some characters during the comparison using the optional data-jso-ignore-regex data attribute. In the below example, the comparison process will ignore all characters except letters and numbers:

<li data-jso-name="Field Name" data-jso-ignore-regex="[^a-zA-Z0-9]+">Field Name</li>

Gallery HTML layout is very flexible and can be changed in any way to match the website style.

Each gallery slide should have data-jso-item="..." attribute. The attribute value should be a unique text string.

The same attribute with the same value should be used in the comparison table to connect the gallery and the table columns.

<!-- gallery slide #1 -->
<div data-jso-item="1">
    <!--... -->
</div>

<!-- gallery slide #2 -->
<div data-jso-item="1">
    <!--... -->
</div>

<!-- ... any numbers of gallery slides ... -->

Comparison Fields

Comparison fields should be defined inside the element with data-jso-features attribute. Each field should have the required data-jso-name attribute and may have an optional data-jso-type attribute.

<!-- ... -->

<div class="jso-hidden" data-jso-compare-table>

    <div data-jso-features>

        <!-- features definitions -->
        <ul>

            <!-- feature #1 -->
            <li data-jso-name="Field Name 1">Field Name 1</li>

            <!-- feature #2 -->
            <li data-jso-name="Field Name 2">Field Name 2</li>

            <!-- feature #3 with HTML data type -->
            <li 
                data-jso-name="Field Name 3" 
                data-jso-type="html">Field Name 3</li>

            <!-- feature #4 with number data type -->
            <li 
                data-jso-name="Field Name 4" 
                data-jso-type="number">Field Name 4</li>

            <!-- any number of fields ... -->
        </ul>
    </div>

    <!-- data content HTML -->
    <!-- .. -->

</div>

In mobile, data-jso-name attribute will be used as an inline title inside each cell.


Comparison Data

Each data column should have data-jso-item attribute, which connects it to the gallery slides.

It can contain multiple data rows, where each row contains data-jso-name attribute that connects it to the data field defined here.

<!-- ... -->

<!-- data content -->
<div>

    <!-- =========================== column #1  =========================== -->
    <div data-jso-item="1">

        <!-- column header -->
        <div class="jso-header">

            <!-- ... any HTML layout ... -->    

            <!-- close button -->
            <button data-jso-close type="button">Close</button>

            <h5>Title</h5>

            <!-- ... any HTML layout ... -->    

        </div>

        <!-- column data list -->
        <ul class="list-group">

            <!-- row #1 -->
            <li class="list-group-item" data-jso-name="Field 1">Value 1</li>

            <!-- row #2 -->
            <li class="list-group-item" data-jso-name="Field 2">Value 2</li>

            <!-- any number of rows here... -->

        </ul>
    </div>

    <!-- =========================== column #2  =========================== -->
    <div data-jso-item="2">

        <!-- column header -->
        <div class="jso-header">

            <!-- ... any HTML layout ... -->    

            <!-- close button -->
            <button data-jso-close type="button">Close</button>

            <h5>Title</h5>

            <!-- ... any HTML layout ... -->    

        </div>

        <!-- column data list -->
        <ul class="list-group">

            <!-- row #1 -->
            <li class="list-group-item" data-jso-name="Field 1">Value 1</li>

            <!-- row #2 -->
            <li class="list-group-item" data-jso-name="Field 2">Value 2</li>

            <!-- any number of rows here... -->

        </ul>
    </div>

    <!-- ... any number of columns ... -->

</div>  

In mobile, data-jso-name attribute will be used as an inline title inside each cell.


Controls

Hide Identical Button

This button is used to hide identical rows. It should have data-jso-hide-identical data attribute.

<!-- hide identical rows button -->
<button type="button" data-jso-hide-identical>Hide Identical</button>

Show All Button

This button is used to show all rows. It should have data-jso-show-all data attribute. By default, it should be hidden.

<!-- show all rows button -->
<button type="button" class="jso-hidden" data-jso-show-all>Show All</button>

Reset Button

This button resets the library to its initial state. It should have data-jso-reset data attribute.

<!-- reset button -->
<button type="button" title="Reset" data-jso-reset>Reset</button>

Close Button

This button is used to hide the data column from the comparison table. Each column should have at least one such button. These buttons are defined by the data-jso-close data attribute.

<!-- close button -->
<button data-jso-close type="button">Close</button>

Settings

Settings List

Property Name Values Default Value Description
containers string, HTMLElement or array of HTMLElements no default value This property defines the container of the whole HTML layout. Read more here.
max number or Infinity 3 This property defines the maximum items number that can be compared. When the number of checked checkboxes reaches the maximum, other checkboxes will be disabled.
slideSpeed number 2 The speed of slide animation in the gallery (px/ms).
fadeDuration number 300 The duration of fade animation in the comparison table (ms).
showAllFeatures boolean true If this property equals false, identical data rows will be hidden on page load.
storage '', 'local-storage', 'session-storage', or 'cookies' '' Storage type. Empty value means that the storage is disabled.
storageName text value 'jso-p-compare' Storage name is used as a key in local / session storage or as a cookie name.
cookiesExpiration number -1 Cookies expiration in minutes (-1 = cookies expire when browser is closed)
hiddenCSSClass text value 'jso-hidden' The CSS class that hides elements.
selectedCSSClass text value 'jso-selected' The CSS class that indicates checked gallery items.
disabledCSSClass text value 'jso-disabled' The CSS class that indicates disabled gallery items.


Containers Property

By default, the library looks for every element on the page that has data-jso-pcompare attribute. Such elements will be interpreted as containers of the product comparison sections automatically.

This can be not enough in some cases, for example, when the page has multiple product comparison sections with different settings.

Below you can find other options to define the containers.

It's possible to use CSS selector, that should be passed to the containers property:

<script>
jsoDControls({
    containers: 'any-css-selector-here', 
    // for example, '.my-container', or '#my-container'


    // other settings...
});
</script>

Another option is to provide HTMLElement directly.

<script>
var container = document.querySelector('#my-container');

jsoDControls({
    containers: container
    // other settings...
});
</script>

It also can be a list of containers:

<script>
var containers = document.querySelectorAll('.my-containers-list');

jsoDControls({
    containers: containers
    // other settings...
});
</script>

The following way is also supported:

<script>
var containers = Array.from(document.querySelectorAll('.my-containers-list'));
containers.push(document.querySelector('#another-container'));

jsoDControls({
    containers: containers
    // other settings...
});
</script>

HTML Data Attributes

Common

Data Attribute HTML Element Values Description
data-jso-pcompare any no value This data attribute defines the whole 'product compare' section. The page may contain multiple instances of the library. Each enstance should have this data attribute.


Data Attribute HTML Element Values Description
data-jso-gallery any no value This data attribute defines the whole gallery container.
data-jso-gallery-holder any no value This data attribute defines the gallery slides container.
data-jso-item any text value This data attribute defines each gallery slide. It connects between the gallery slide and the comparison column.
data-jso-compare-cb checkbox no value This data attribute defines the gallery checkboxes / toggle buttons.
data-jso-left-btn button no value Left gallery button.
data-jso-right-btn button no value Right gallery button.


Comparison

Data Attribute HTML Element Values Description
data-jso-compare-table any no value This data attribute defines the comparison part of the library.
data-jso-features any no value This data attribute defines the features/fields definition container.
data-jso-name any text value This data attribute connects between feature/field name and feature value.
data-jso-type any 'text', 'html', 'number' This attribute defines field data type.
data-jso-case-sensitive any no value If this attribute exists, this field comparison will be case-sensitive. By default, the comparison is not case-sensitive. This attribute can be applied only to text fields.
data-jso-ignore-regex JavaScript regex text value, for example
'[^0-9.]+'
This attribute allows ignoring some characters during field comparison. This attribute can be applied only to text fields.
data-jso-item any text value This data attribute connects between the gallery slide and the comparison column.


Control Buttons

Data Attribute HTML Element Values Description
data-jso-hide-identical button no value Once pressed, this button hides identical data rows.
data-jso-show-all button no value Once pressed, this button shows identical data rows.
data-jso-reset button no value This button resets the library to its initial state.
data-jso-close button no value This button removes the data column it belongs to.



CSS Classes

Class Name Description
jso-selected Each time the checkbox is checked, the class jso-selected will be added to the appropriate gallery slide (defined by the data-jso-item data attribute).
jso-disabled Each time the checkbox is disabled, the class jso-disabled will be added to the appropriate gallery slide (defined by the data-jso-item data attribute).
jso-hidden This CSS class is used to hide HTML elements.

Storage

The library uses storage to save the current state and restore it after page refresh or when the back button is pressed. The storage setting is optional.

There are three possible storages that the library supports: local storage, session storage, and cookies.

Local Storage

Local storage is persistent storage that keeps data even if the user closes the browser. It will be cleared only when the user clears the browser cache.

<script>
jsoDControls({
    storage: 'local-storage',
    storageName: 'jso-p-compare',
    // other settings...
});
</script>

Session Storage

Session storage will be cleared when the browser session is over, typically when the user closes the browser.

<script>
jsoDControls({
    storage: 'session-storage',
    storageName: 'jso-p-compare',
    // other settings...
});
</script>

Cookies

In this case, the state will be saved in cookies. Unlike local and session storage, cookie values can be used on the server too. It's possible to define cookies' expiration date.

<script>
jsoDControls({
    storage: 'cookies',
    storageName: 'jso-p-compare',
    cookiesExpiration: -1, // cookies expiration in minutes (-1 = cookies expire when browser is closed)
    // other settings...
});
</script>

How-To

Pre-Selected Checkboxes

Gallery items can be pre-selected on page load using the standard checked attribute.

<!-- gallery item -->
<div data-jso-item="1">

    <!-- HTML layout before ... -->

    <input type="checkbox" data-jso-compare-cb checked />

    <!-- HTML layout after ... -->

</div>

How to change the animation speed

The library uses modern Web Animations API for gallery slide animation and for fade in/out animation inside the comparison table.


How to change the gallery slide animation?

To change the speed of the gallery slide animation, use the following setting:

pCompare({
    slideSpeed: 2 // pixels in millisecond
});

This value will be adjusted automatically, depending on the viewport size.


How to change the fade in/out animation?

pCompare({
    fadeDuration: 300 // millisecond
});