Saturday, January 25, 2014

How to build an interface with flexible horizontal slider for browsing through multiple stories at a time, like the one used by CheckDeck CheckDeck.com?

In the name of CheckDeck, we are building a beautiful place to discover new exciting things everyday. It's first product Storiesallows you to explore exciting stories from all around the world just at one place.

To create a pleasant content discovery experience, we have taken extreme care and put effort to build a simple and wonderful user interface. Some of the noteworthy features that you will find on the home page of CheckDeck Stories are:
  1. A layout which tries to fully utilize your display screen width and height by arranging the content dynamically based on the dimensions of your screen. Specifically,
    1. It takes into consideration the width of your display by calculating the number of story tiles it can fit to be displayed at a time. When your browser's window is resized, it will recalculate the number of stories which can be shown in each horizontal row per topic and rearrange the layout accordingly.
    2. The layout tries to fill up the whole window in vertical dimension by showing stories from more number of topics until the screen is filled up vertically with stories. As you scroll to the bottom, stories on more topics will be loaded and displayed.
  2. It allows you to browse through large number of stories on each topic without changing the page by presenting them in what we can consider as being inspired from a slider or gallery like layout which is quite popular in photo sharing sites to create a visually delightful experience in addition to facilitating a quick browsing experience. Specifically, 
    1. We show two button like icons on both sides of the array of story tiles in each row. By clicking on the right arrow icon(when visible), it will show you stories next in the order. By clicking on the left arrow icon(when visible), will show you previous stories in the order. The icon with cloud like symbol with a downward pointing arrow just after clicking on any of the left or right arrow icons suggests that there is some activity being carried out (like making the transition of stories by replacing the current set of stories by next or previous ones, preloading images for the stories next in the order, loading next stories further down the line in order on this topic or any other activity) which needs to be completed before re enabling the left and/or right icons to allow the user to browse the stories again in either direction.
    2. To make the layout touch screen friendly, we have enabled stories browsing by swiping over the stories row to browse to left or right for viewing next or previous stories in order.
    3. When clicked on any story tile, it opens a view where we display the essential details of the article like title, published time, source, RSS/Atom feed content and description etc. We also link the title displayed in this view to open the article on its publishers site in new tab.
I am sharing with you the file from CheckDeck which currently arranges the layout of which I am talking about. Forgive me about formatting, I will make a viewer friendly formatting of this code as soon as get chance.
  • It uses require.js as the inter module dependency manager.
  • The other modules being included as a dependency contribute for downloading content etc. But the ideas which I am talking about is mainly included in this code snippet.
  • It uses jQuery's drop effect for transition of stories, other animations can also be used like swipe effect.
  • We have implemented this in JavaScript. But the logic of this technique can also be adapted in different programming languages to create similar visual effect on various platforms like Android, iOS, Windows etc.
  • If time permits, I will share this code something like a jQuery plugin which can be used and edited as per your application specific requirements. Till then, I hope this will work as a reference implementation for you.
define(["./entitiesData", "./windowResized", "./friendlyPublishedTime", "./loadingImages", "./storiesOnEntityData", "./browserCompatibility", "./header"], function(entitiesData, windowResized, friendlyPublishedTime, loadingImages, storiesOnEntityData, browserCompatibility, header) {
    var headerHeight = header.getHeight();
    var numberOfStoriesToBeDisplayed;
    var numberOfDisplayableStoryTiles;
    var storyTileWidth = 250;
    var spacingBetweenStoryTiles = 10;
    var buttonWidth = 64;
    var totalSpacingAroundEachButton = 14;
    var indicatorStripWidth = 3;
    var marginForScrollbar = 20;
    var storyRowBigRepresentiveImageWidth = 100;
    var storyRowTinyRepresentiveImageWidth = 64;
    var viewDOM;
    var layoutToBeShown;
    var isTinyScreen;
    var numberOfStoryRowsToBeDisplayedPerEntity = 3;

    function show(viewDOMReceived, viewInformation) {
        viewDOM = viewDOMReceived;
        header.showMainMenu();
        viewDOM.attr("id", "homeView").append(

        $("<table id='storiesOnEntitiesTable'/>")

        );
        arrangeLayout();
        entitiesData.getFollowedEntities(function(error, entities) {
            if (error) {
                console.error("When requested to get followed entities received: " + error.stack);
            }
            else {
                storiesOnEntityData.startPreparingStories(entities, function(error) {
                    if (error) {
                        console.error("When requested to start preparing stories on entities:" + JSON.stringify(entities, undefined, 2) + " received: " + error.stack);
                    }
                });
                entities.forEach(function(entity) {
                    entity.hasBeenDisplayed = false;
                });
                if (viewInformation && viewInformation.entityBeingExplored) {
                    (function showOneMoreEntity() {
                        showStoriesOnMoreEntities(1, function() {
                            console.log("Shown stories on one more entity.");
                            if (viewInformation.entityBeingExplored.hasBeenDisplayed) {
                                fillUpWindow(function() {
                                    $(window).scrollTop(viewInformation.scrollTop);
                                    arrangeLayout();
                                });
                            }
                            else {
                                showOneMoreEntity();
                            }
                        });
                    })();
                }
                else {
                    fillUpWindow(function() {
                        arrangeLayout();
                    });
                }
            }
        });
    }

    function handleViewResized() {
        // if ($("#storiesOnEntitiesTable").height() + headerHeight < window.innerHeight) {
        //     fillUpWindow();
        // }
        arrangeLayout();

        $("#storiesOnEntitiesTable .entityRow").remove();
        entitiesData.getFollowedEntities(function(error, entities) {
            if (error) {
                console.error("When requested to get followed entities received: " + error.stack);
            }
            else {
                entities.forEach(function(entity) {
                    if (entity.hasBeenDisplayed) {
                        displayEntityRow(entity);
                    }
                });
            }
        });
    }

    function arrangeLayout() {
        numberOfDisplayableStoryTiles = getNumberOfDisplayableStoryTiles();

        if ($(window).innerWidth() > 700) {
            layoutToBeShown = 'tile';
            isTinyScreen = false;
            $("head").append(

            $("<style type='text/css'/>").text(

            "#storiesOnEntitiesTable, #storiesOnEntitiesTable .storiesOnEntityTable {width: auto}" +

            "#storiesOnEntitiesTable .entityRow {height: 433px}" +

            ".cellOfButtonForBrowsingStories {display: table-cell}" +

            "#storiesOnEntitiesTable .entityRow .entityBox .header .emptyCell {display: table-cell}" +

            "#storiesOnEntitiesTable .entityRow .entityBox .header .emptyCell {display: table-cell}"

            )

            );
        }
        else {
            layoutToBeShown = 'row';
            if ($(window).innerWidth() < 500) {
                isTinyScreen = true;
                $("head").append(

                $("<style type='text/css'/>").text(

                "#storiesOnEntitiesTable, #storiesOnEntitiesTable .storiesOnEntityTable {width: 100%}" +

                ".storyRowsTable{width: " + (viewDOM.innerWidth() - 2 * (indicatorStripWidth) - marginForScrollbar) + "px}" +

                ".storyRowsTable .storyRow .textualDetailsOfStoryCell{max-width: " + (viewDOM.innerWidth() - 2 * (indicatorStripWidth) - storyRowTinyRepresentiveImageWidth - marginForScrollbar) + "px}" +

                "#storiesOnEntitiesTable .entityRow {height: auto}"

                )

                );
            }
            else {
                isTinyScreen = false;
                $("head").append(

                $("<style type='text/css'/>").text(

                "#storiesOnEntitiesTable, #storiesOnEntitiesTable .storiesOnEntityTable {width: 100%}" +

                ".storyRowsTable{width: " + (viewDOM.innerWidth() - 2 * (buttonWidth + totalSpacingAroundEachButton) - marginForScrollbar) + "px}" +

                ".storyRowsTable .storyRow .textualDetailsOfStoryCell{max-width: " + (viewDOM.innerWidth() - 2 * (buttonWidth + totalSpacingAroundEachButton) - storyRowBigRepresentiveImageWidth - marginForScrollbar) + "px}" +

                " #storiesOnEntitiesTable .entityRow {height: auto}"

                )

                );
            }
        }
    }

    function fillUpWindow(doneFillingUpWindow) {
        showStoriesOnMoreEntities(3, function() {
            if ($("#storiesOnEntitiesTable").height() + headerHeight < $(window).innerHeight() + marginForScrollbar) {
                fillUpWindow(doneFillingUpWindow);
            }
            else if (typeof doneFillingUpWindow === 'function') {
                doneFillingUpWindow();
            }
        });
    }

    function handleWindowScrolled() {
        var y = $(window).scrollTop(); // (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
        if (($(window).innerHeight() + marginForScrollbar + y) >= $("#storiesOnEntitiesTable").height() + headerHeight) {
            console.log("Reached to bottom of the display");
            showStoriesOnMoreEntities(3);
        }
        else {
            console.log("We haven't yet reached to the bottom.");
        }
    }

    var ongoingShowingStoriesOnMoreEntities = false;

    function showStoriesOnMoreEntities(numberOfMoreEntitiesToBeShown, doneShowingStoriesOnMoreEntities) {
        if (!ongoingShowingStoriesOnMoreEntities) {
            ongoingShowingStoriesOnMoreEntities = true;
            entitiesData.getFollowedEntities(function(error, entities) {
                if (error) {
                    console.error("When requested to get followed entities received: " + error.stack);
                }
                else {
                    for (var i = 0; i < entities.length && numberOfMoreEntitiesToBeShown > 0; i++) {
                        if (!entities[i].hasBeenDisplayed) {
                            displayEntityRow(entities[i], i);
                            entities[i].hasBeenDisplayed = true;
                            numberOfMoreEntitiesToBeShown--;
                        }
                    }
                }
                ongoingShowingStoriesOnMoreEntities = false;
                if (typeof doneShowingStoriesOnMoreEntities === 'function') {
                    doneShowingStoriesOnMoreEntities();
                }
            });
        }
    }
    var imagesStorageLocation = $("meta[name=imagesStorageLocation]").attr("content");

    function displayEntityRow(entity, position) {
        var entityRowDOM = getEntityRowHTML();
        if ($("#storiesOnEntitiesTable > .entityRow").length) {
            $("#storiesOnEntitiesTable > .entityRow").eq(position - 1).after(entityRowDOM);
        }
        else {
            $("#storiesOnEntitiesTable").append(entityRowDOM);
        }

        function getEntityRowHTML() {

            return $("<tr class='entityRow' data-entityID='" + entity.entityID + "'/>").append(

            $("<td/>").append(

            $("<table class='entityBox'/>").append(

            $("<tr class='header'/>").append(

            $("<td class='emptyCell'/>")

            ).append(

            $("<td/>").append(

            $("<table class='entityHeaderTable'/>").append(

            $("<tr/>").append(

            $("<td/>").append(

            $("<span class='entityName'/>").text(entity.name)

            )

            )

            )


            )

            ).append(

            $("<td class='emptyCell'/>")

            )

            )

            )

            );

        }
        entityRowDOM.find(".entityName").click(function() {
            callbacksToHandleRequestForOpeningExploreStoriesOnEntityView.forEach(function(callback) {
                callback({
                    entityBeingExplored: entity,
                    scrollTop: $(window).scrollTop()
                }, entity);
            });
        });
        (function showStoriesOnEntity() {
            var browsableStoriesOnEntityRow = $("<tr class='storiesOnEntityBrowser'/>").append(

            $("<td class='cellOfButtonForBrowsingStories'/>").addClass(isTinyScreen ? 'indicatorStrip' : 'roundButton').append(

            $("<span class='leftButtonForBrowsingStories hidden'/>")

            )

            );

            $("#storiesOnEntitiesTable .entityRow[data-entityID='" + entity.entityID + "'] .entityBox").append(

            browsableStoriesOnEntityRow

            );

            var noStoriesMessage = "Sorry, we don't have any stories at present on this.";
            browsableStoriesOnEntityRow.append(

            $("<td class='loadingStoriesMessageCell'/>").append(

            $("<span class='loadingStoriesOnEntityMessage'/>").text("Loading stories")

            )

            );
            storiesOnEntityData.loadStories(entity, function(error) {
                browsableStoriesOnEntityRow.find(".loadingStoriesMessageCell").remove();
                if (error || entity.stories.length === 0) {
                    browsableStoriesOnEntityRow.append(

                    $("<td/>").append(

                    $("<span class='noStoriesOnEntityMessage'/>").text(noStoriesMessage)

                    )

                    );

                    browsableStoriesOnEntityRow.append(

                    $("<td class='cellOfButtonForBrowsingStories'/>").addClass(isTinyScreen ? 'indicatorStrip' : 'roundButton').append(

                    entity.currentStoryBrowsingPosition > 0 ? $("<span class='leftButtonForBrowsingStories disabled'/>") : $("<span class='leftButtonForBrowsingStories hidden'/>")

                    )

                    );
                    $("#storiesOnEntitiesTable .entityRow[data-entityID='" + entity.entityID + "'] .entityBox").append(

                    browsableStoriesOnEntityRow

                    );
                }
                else {
                    var storiesOnEntityDOMElement = $("<table class='storiesOnEntityTable'/>").append(

                    $("<tr class='storiesOnEntityRow'/>")

                    );
                    browsableStoriesOnEntityRow.append(

                    $("<td/>").append(

                    storiesOnEntityDOMElement

                    )

                    );
                    if (typeof entity.currentStoryBrowsingPosition !== 'number') {
                        entity.currentStoryBrowsingPosition = 0;
                    }
                    var sourceEntitiesToStartPreparingStoriesFor = [];
                    var i = entity.currentStoryBrowsingPosition;
                    var remainingNumberOfStoriesToBeDisplayed;
                    var story;
                    if (layoutToBeShown === 'tile') {
                        remainingNumberOfStoriesToBeDisplayed = numberOfStoriesToBeDisplayed = numberOfDisplayableStoryTiles;
                        while (i < entity.stories.length && remainingNumberOfStoriesToBeDisplayed > 0) {
                            story = entity.stories[i];
                            storiesOnEntityDOMElement.find(".storiesOnEntityRow").append(

                            $("<td class='storyCell'/>").append(

                            $("<span class='storyTile'/>").html(

                            getHTMLForStoryTile(entity, story)

                            )

                            )

                            );
                            sourceEntitiesToStartPreparingStoriesFor.push({
                                entityID: story.sourceEntityID,
                                type: "source",
                                name: story.sourceName
                            });
                            i++;
                            remainingNumberOfStoriesToBeDisplayed--;
                        }
                    }
                    else {
                        //layoutToBeShown === 'row'
                        remainingNumberOfStoriesToBeDisplayed = numberOfStoriesToBeDisplayed = numberOfStoryRowsToBeDisplayedPerEntity;
                        var storyRowsTableDOM = $("<table class='storyRowsTable'/>");
                        storiesOnEntityDOMElement.find(".storiesOnEntityRow").append(storyRowsTableDOM);
                        while (i < entity.stories.length && remainingNumberOfStoriesToBeDisplayed > 0) {
                            story = entity.stories[i];

                            storyRowsTableDOM.append(

                            $("<tr class='storyRow'/>").html(

                            getHTMLForStoryRow(entity, story)

                            )

                            );
                            sourceEntitiesToStartPreparingStoriesFor.push({
                                entityID: story.sourceEntityID,
                                type: "source",
                                name: story.sourceName
                            });
                            i++;
                            remainingNumberOfStoriesToBeDisplayed--;
                        }
                    }
                    storiesOnEntityData.startPreparingStories(sourceEntitiesToStartPreparingStoriesFor, function(error) {
                        if (error) {
                            console.error("When requested to start preparing stories on entities:" + JSON.stringify(sourceEntitiesToStartPreparingStoriesFor, undefined, 2) + " received: " + error.stack);
                        }
                    });
                    browsableStoriesOnEntityRow.append(

                    $("<td class='cellOfButtonForBrowsingStories'/>").addClass(isTinyScreen ? 'indicatorStrip' : 'roundButton').append(

                    entity.stories.length > numberOfStoriesToBeDisplayed + entity.currentStoryBrowsingPosition ? $("<span class='rightButtonForBrowsingStories disabled'/>") : $("<span class='rightButtonForBrowsingStories hidden'/>")

                    )

                    );
                    var enableButtonForBrowsing = function(buttonDOM) {
                        buttonDOM.addClass("enabled").removeClass("disabled").removeClass("hidden");
                    };

                    var disableButtonForBrowsing = function(buttonDOM) {
                        buttonDOM.removeClass("enabled").addClass("disabled").removeClass("hidden");
                    };

                    var hideButtonForBrowsing = function(buttonDOM) {
                        buttonDOM.removeClass("enabled").removeClass("disabled").addClass("hidden");
                    };
                    entity.browsingToRightEnabled = false;
                    if (entity.stories.length > numberOfStoriesToBeDisplayed + entity.currentStoryBrowsingPosition) {
                        var storiesWithImages = [];
                        for (var m = entity.currentStoryBrowsingPosition + numberOfStoriesToBeDisplayed; m < entity.stories.length && m < (entity.currentStoryBrowsingPosition + 2 * numberOfStoriesToBeDisplayed); m++) {
                            if (typeof entity.stories[m].imageR1 === 'string' && entity.stories[m].imageR1.trim().length) {
                                storiesWithImages.push(entity.stories[m]);
                            }
                        }
                        if (isTinyScreen) {
                            //Don't wait for images to be loaded before enabling
                            //browse to right button
                            loadingImages.loadRepresentativeImages(storiesWithImages, "imageR1", function() {});
                            enableButtonForBrowsing(browsableStoriesOnEntityRow.find(".rightButtonForBrowsingStories"));
                            entity.browsingToRightEnabled = true;
                        }
                        else {
                            loadingImages.loadRepresentativeImages(storiesWithImages, "imageR1", function() {
                                enableButtonForBrowsing(browsableStoriesOnEntityRow.find(".rightButtonForBrowsingStories"));
                                entity.browsingToRightEnabled = true;
                            });
                        }
                    }
                    if (entity.currentStoryBrowsingPosition > 0) {
                        enableButtonForBrowsing(browsableStoriesOnEntityRow.find(".leftButtonForBrowsingStories"));
                        entity.browsingToLeftEnabled = true;
                    }
                    else {
                        entity.browsingToLeftEnabled = false;
                    }
                    var browseStories = function(direction, doneBrowsingStories) {
                        var sourceEntitiesToStartPreparingStoriesFor = [];
                        var remainingNumberOfStoriesToBeDisplayed = numberOfStoriesToBeDisplayed;
                        if (layoutToBeShown === 'row') {
                            var storyRowsTableDOM = $("<table class='storyRowsTable'/>");

                            for (var l = entity.currentStoryBrowsingPosition; remainingNumberOfStoriesToBeDisplayed > 0 && l < entity.stories.length;) {
                                storyRowsTableDOM.append(

                                $("<tr class='storyRow'/>").html(getHTMLForStoryRow(entity, entity.stories[l]))

                                );
                                sourceEntitiesToStartPreparingStoriesFor.push({
                                    entityID: entity.stories[l].sourceEntityID,
                                    type: "source",
                                    name: entity.stories[l].sourceName
                                });
                                l++;
                                remainingNumberOfStoriesToBeDisplayed--;
                            }

                            $(browsableStoriesOnEntityRow).find(".storyRowsTable").effect(

                            "drop",

                            {
                                direction: direction === 'right' ? 'left' : 'right'
                            },

                            300,

                            function() {
                                $(this).remove();
                                $(browsableStoriesOnEntityRow).find(".storiesOnEntityRow").append(storyRowsTableDOM.hide().fadeIn(300));
                                doneBrowsingStories();
                            });
                        }
                        else {
                            $(browsableStoriesOnEntityRow).find(".storyCell").addClass("storyToBeHidden");

                            var remainingNumberOfStoriesToBeDropped = $(browsableStoriesOnEntityRow).find(".storyToBeHidden").length;

                            var checkForRemovalOfAllTheStoriesToBeDropped = function() {
                                remainingNumberOfStoriesToBeDropped--;
                                if (remainingNumberOfStoriesToBeDropped === 0) {
                                    doneBrowsingStories();
                                }
                            }

                            for (var k = entity.currentStoryBrowsingPosition; remainingNumberOfStoriesToBeDisplayed > 0 && k < entity.stories.length;) {
                                if ($(browsableStoriesOnEntityRow).find(".storyToBeHidden").length) {

                                    $(browsableStoriesOnEntityRow).find(".storyToBeHidden").eq(0).find(".storyTileTable").css("z-index", "1").effect(

                                    "drop",

                                    {
                                        direction: direction === 'right' ? 'left' : 'right'
                                    },

                                    300,

                                    function() {
                                        $(this).remove();
                                        checkForRemovalOfAllTheStoriesToBeDropped();
                                    }

                                    );
                                    $(browsableStoriesOnEntityRow).find(".storyToBeHidden").eq(0).removeClass("storyToBeHidden").find(".storyTile").append(getHTMLForStoryTile(entity, entity.stories[k]));
                                }
                                else {
                                    //Add storyCells if required
                                    $(browsableStoriesOnEntityRow).find(".storiesOnEntityRow").append($("<td class='storyCell'/>").append($("<span class='storyTile'/>").html(getHTMLForStoryTile(entity, entity.stories[k]))));
                                }
                                sourceEntitiesToStartPreparingStoriesFor.push({
                                    entityID: entity.stories[k].sourceEntityID,
                                    type: "source",
                                    name: entity.stories[k].sourceName
                                });
                                k++;
                                remainingNumberOfStoriesToBeDisplayed--;
                            }
                            //remove remaining storyCells to be hidden
                            if ($(browsableStoriesOnEntityRow).find(".storyToBeHidden").length) {
                                $(browsableStoriesOnEntityRow).find(".storyToBeHidden").find(".storyTileTable").effect("drop", {
                                    direction: direction === 'right' ? 'left' : 'right'
                                },
                                300,

                                function() {
                                    $(browsableStoriesOnEntityRow).find(".storyToBeHidden").remove();
                                    checkForRemovalOfAllTheStoriesToBeDropped();
                                });
                            }
                            storiesOnEntityData.startPreparingStories(sourceEntitiesToStartPreparingStoriesFor, function(error) {
                                if (error) {
                                    console.error("When requested to start preparing stories on entities:" + JSON.stringify(sourceEntitiesToStartPreparingStoriesFor, undefined, 2) + " received: " + error.stack);
                                }
                            });
                        }

                    };

                    var browsingToLeftButtonDOM = $(browsableStoriesOnEntityRow).find(".leftButtonForBrowsingStories");
                    var browsingToRightButtonDOM = $(browsableStoriesOnEntityRow).find(".rightButtonForBrowsingStories");
                    var handleRequestForBrowsingToRight = function(event) {
                        if (entity.browsingToRightEnabled) {
                            entity.browsingToRightEnabled = false;
                            disableButtonForBrowsing(browsingToLeftButtonDOM);
                            entity.browsingToLeftEnabled = false;
                            disableButtonForBrowsing(browsingToRightButtonDOM);
                            entity.currentStoryBrowsingPosition += numberOfStoriesToBeDisplayed;
                            entity.doneWithTransitionOfStories = false;
                            var enableOrDisableButtonsForBrowsing = function() {
                                if (entity.doneWithTransitionOfStories) {
                                    entity.browsingToLeftEnabled = true;
                                    enableButtonForBrowsing(browsingToLeftButtonDOM);
                                    if (entity.currentStoryBrowsingPosition + numberOfStoriesToBeDisplayed < entity.stories.length) {
                                        if (isTinyScreen) {
                                            entity.browsingToRightEnabled = true;
                                            enableButtonForBrowsing(browsingToRightButtonDOM);
                                        }
                                        else if (entity.doneWithLoadingImagesOfNextStories) {
                                            entity.browsingToRightEnabled = true;
                                            enableButtonForBrowsing(browsingToRightButtonDOM);
                                        }
                                    }
                                }
                            };
                            browseStories("right", function() {
                                entity.doneWithTransitionOfStories = true;
                                enableOrDisableButtonsForBrowsing();
                            });
                            if (entity.currentStoryBrowsingPosition + numberOfStoriesToBeDisplayed < entity.stories.length) {
                                entity.doneWithLoadingImagesOfNextStories = false;
                                var storiesWithImages = [];
                                for (var i = entity.currentStoryBrowsingPosition + numberOfStoriesToBeDisplayed; i < entity.stories.length && i < (entity.currentStoryBrowsingPosition + 2 * numberOfStoriesToBeDisplayed); i++) {
                                    if (typeof entity.stories[i].imageR1 === 'string' && entity.stories[i].imageR1.trim().length) {
                                        storiesWithImages.push(entity.stories[i]);
                                    }
                                }
                                loadingImages.loadRepresentativeImages(storiesWithImages, "imageR1", function() {
                                    entity.doneWithLoadingImagesOfNextStories = true;
                                    if (!isTinyScreen) {
                                        enableOrDisableButtonsForBrowsing();
                                    }
                                });
                            }
                            else {
                                hideButtonForBrowsing(browsingToRightButtonDOM);
                            }
                        }
                    };
                    Hammer($(browsableStoriesOnEntityRow).find(".storiesOnEntityRow")[0], {
                        swipe_velocity: 0.2
                    }).on("swipeleft", handleRequestForBrowsingToRight);
                    browsingToRightButtonDOM.on("click", handleRequestForBrowsingToRight);
                    var handleRequestForBrowsingToLeft = function() {
                        if (entity.browsingToLeftEnabled) {
                            entity.browsingToRightEnabled = false;
                            disableButtonForBrowsing(browsingToLeftButtonDOM);
                            entity.browsingToLeftEnabled = false;
                            disableButtonForBrowsing(browsingToRightButtonDOM);
                            entity.currentStoryBrowsingPosition = Math.max(entity.currentStoryBrowsingPosition - numberOfStoriesToBeDisplayed, 0);
                            entity.doneWithTransitionOfStories = false;
                            var enableOrDisableButtonsForBrowsing = function() {
                                if (entity.doneWithTransitionOfStories) {
                                    entity.browsingToRightEnabled = true;
                                    enableButtonForBrowsing(browsingToRightButtonDOM);
                                    if (Math.max(entity.currentStoryBrowsingPosition - numberOfStoriesToBeDisplayed, 0) !== entity.currentStoryBrowsingPosition) {
                                        entity.browsingToLeftEnabled = true;
                                        enableButtonForBrowsing(browsingToLeftButtonDOM);
                                    }
                                }
                            };
                            browseStories("left", function() {
                                entity.doneWithTransitionOfStories = true;
                                enableOrDisableButtonsForBrowsing();
                            });
                            if (Math.max(entity.currentStoryBrowsingPosition - numberOfStoriesToBeDisplayed, 0) !== entity.currentStoryBrowsingPosition) {}
                            else {
                                hideButtonForBrowsing(browsingToLeftButtonDOM);
                            }
                        }
                    };
                    Hammer($(browsableStoriesOnEntityRow).find(".storiesOnEntityRow")[0], {
                        swipe_velocity: 0.2
                    }).on("swiperight", handleRequestForBrowsingToLeft);
                    browsingToLeftButtonDOM.on("click", handleRequestForBrowsingToLeft);
                }
            });

        })();
    }

    function getHTMLForStoryTile(entity, story) {
        var storyTileTable = $("<table class='storyTileTable'/>");
        if (typeof story.imageR1 === 'string' && story.imageR1.trim().length) {
            storyTileTable.addClass("storyTileWithRepresentativeImage").append(

            $("<tr/>").append(

            $("<td/>").append(

            $("<span class='representativeImageWrapper'/>").append(

            $("<img class='representativeImage' src='http://" + imagesStorageLocation + "/" + story.imageR1 + "'/>")

            ).append(

            $("<a class='openArticleInNewTabIcon'/>").attr("href", story.articleURL).attr("target", "_blank").attr("title", "View full article").hide()

            )

            )

            )

            ).append(

            $("<tr/>").append(

            $("<td/>").append(

            $("<span class='title'/>").text(story.title)

            )

            )

            );

        }
        else {
            storyTileTable.addClass("storyTileWithoutRepresentativeImage").append(

            $("<tr class='emptyRowForMargin'/>")

            ).append(

            $("<tr/>").append(

            $("<td class='title'/>").text(story.title).append(

            $("<a class='openArticleInNewTabIcon'/>").attr("href", story.articleURL).attr("target", "_blank").hide()

            )

            )

            ).append(

            $("<tr class='emptyRowForMargin'/>")

            );
        }
        storyTileTable.append(

        $("<tr/>").append(

        $("<td class='sourceNameCell'/>").append(

        $("<span class='sourceName'/>").attr("data-entityID", story.sourceEntityID).attr("data-name", story.sourceName).text(story.sourceName)

        )

        )

        ).append(

        $("<tr/>").append(

        $("<td/>").append(

        $("<span class='publishedTime'/>").text(friendlyPublishedTime.getInFriendlyFormat(story.publishedDate))

        )

        )

        );

        storyTileTable.mouseenter(function() {
            $(this).find(".openArticleInNewTabIcon").fadeIn(300);
        });
        storyTileTable.mouseleave(function() {
            $(this).find(".openArticleInNewTabIcon").fadeOut(300);
        });
        storyTileTable.find(".representativeImage, .title").on("click", function(event) {
            callbacksToHandleRequestForOpeningFullArticleView.forEach(function(callback) {
                callback({
                    entityBeingExplored: entity,
                    scrollTop: $(window).scrollTop()
                }, entity, story);
            });
        });
        storyTileTable.find(".sourceName").click(function(event) {
            callbacksToHandleRequestForOpeningExploreStoriesOnEntityView.forEach(function(callback) {
                callback({
                    entityBeingExplored: entity,
                    scrollTop: $(window).scrollTop()
                }, {
                    entityID: $(event.target).attr("data-entityID"),
                    type: "source",
                    name: $(event.target).attr("data-name")
                });
            });
        });
        return storyTileTable;
    }

    function getHTMLForStoryRow(entity, story) {
        var storyRowCellDOMs = [];
        var imageToBeShown = null;
        if (isTinyScreen && typeof story.imageR3 === 'string' && story.imageR3.trim().length) {
            imageToBeShown = "imageR3";
        }
        else if (typeof story.imageR2 === 'string' && story.imageR2.trim().length) {
            imageToBeShown = "imageR2";
        }
        else if (typeof story.imageR1 === 'string' && story.imageR1.trim().length) {
            imageToBeShown = "imageR1";
        }
        if (imageToBeShown) {
            storyRowCellDOMs.push($("<td class='representativeImageCell'/>").append(

            $("<img class='representativeImage'/>").attr("src", "http://" + imagesStorageLocation + "/" + story[imageToBeShown]).addClass(isTinyScreen ? "forTinyScreen" : "forNotTinyScreen")

            ));
        }
        storyRowCellDOMs.push($("<td class='textualDetailsOfStoryCell' colspan='2'/>").append(

        $("<span class='title'/>").text(story.title)

        ).append(

        $("<span class='sourceName'/>").text(story.sourceName).attr("data-entityID", story.sourceEntityID).attr("data-name", story.sourceName)

        ).append(

        $("<span class='publishedTime'/>").text(friendlyPublishedTime.getInFriendlyFormat(story.publishedDate))

        ).append(

        $("<a class='openArticleInNewTabLink'/>").attr("href", story.articleURL).attr("target", "_blank").text("View full story")

        ));
        storyRowCellDOMs.forEach(function(storyRowCellDOM) {
            storyRowCellDOM.find(".representativeImage, .title").on("click", function(event) {
                callbacksToHandleRequestForOpeningFullArticleView.forEach(function(callback) {
                    callback({
                        entityBeingExplored: entity,
                        scrollTop: $(window).scrollTop()
                    }, entity, story);
                });
            });
            storyRowCellDOM.find(".sourceName").click(function(event) {
                callbacksToHandleRequestForOpeningExploreStoriesOnEntityView.forEach(function(callback) {
                    callback({
                        entityBeingExplored: entity,
                        scrollTop: $(window).scrollTop()
                    }, {
                        entityID: $(event.target).attr("data-entityID"),
                        type: "source",
                        name: $(event.target).attr("data-name")
                    });
                });
            });
        });

        return storyRowCellDOMs;
    }

    function getNumberOfDisplayableStoryTiles() {
        //Important: Keep this values in sync with those with style.css
        //to carry out accurate calculations for preparing layout

        var availableWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
        return Math.floor((availableWidth - 2 * (buttonWidth + totalSpacingAroundEachButton) - spacingBetweenStoryTiles) / (storyTileWidth + spacingBetweenStoryTiles));
    }


    var callbacksToHandleRequestForOpeningExploreStoriesOnEntityView = [];

    function registerCallbackToHandleRequestForOpeningExploreStoriesOnEntityView(callback) {
        callbacksToHandleRequestForOpeningExploreStoriesOnEntityView.push(callback);
    }

    var callbacksToHandleRequestForOpeningFullArticleView = [];

    function registerCallbackToHandleRequestForOpeningFullArticleView(callback) {
        callbacksToHandleRequestForOpeningFullArticleView.push(callback);
    }
    return {
        show: show,
        registerCallbackToHandleRequestForOpeningFullArticleView: registerCallbackToHandleRequestForOpeningFullArticleView,
        registerCallbackToHandleRequestForOpeningExploreStoriesOnEntityView: registerCallbackToHandleRequestForOpeningExploreStoriesOnEntityView,
        handleViewResized: handleViewResized,
        handleWindowScrolled: handleWindowScrolled
    };
});

Here are some of the snapshots from different versions of CheckDeck using this technique described above.
The newer version: (released on 26th January 2014)


The older version: (Released in March 2013)


Creative Commons License
Horizontal slider for browsing through multiple stories at a time by Atharva Patel for CheckDeck is licensed under a Creative Commons Attribution 4.0 International License.
Based on a work at http://CheckDeck.com.