0

I'm building a simple slideshow that is controlled by buttons when viewed on a computer and by swiping gestures on touch screen devices. This is a demo with 3 images.

Each image, its corresponding caption and the navigation are contained within one div. Here's the first one:

<div class="item" id="1">
    <img src="...">
    <div class="caption">
        caption 1
    </div>
    <div class="navigation">
        <a href="#" id="1prev">&lt</a> 1 / 3 <a href="#" id="1next">&gt</a>
    </div>
</div>

These divs are shown or hidden using the "click" and "swipeleft / swiperight" functions.

$(document).ready(function () {
    $("#1prev").click(function () {
        $("#1").hide();
        $("#3").show();
    });
    $("#1").on("swipeleft", function () {
        $("#1").hide();
        $("#3").show();
    });
    $("#1next").click(function () {
        $("#1").hide();
        $("#2").show();
    });
    $("#1").on("swiperight", function () {
        $("#1").hide();
        $("#2").show();
    });
});

The slideshow will contain as many as 40 images in total. Is there a way to condense the script? Is this a relatively efficient and accessible solution? Is the code written properly? Can it be improved?

D Benway
  • 65
  • 1
  • 2
  • 10
  • http://jsfiddle.net/ej40c8m7/ – Ciccolina Mar 11 '15 at 21:22
  • Thank you. How can I add the "click" function of the buttons into that code? When I reach the end of the array, how can I make it loop back to the beginning? What function can I call to show the next image or previous image without the fade in / out? – D Benway Mar 11 '15 at 22:31

1 Answers1

1

You could do something like this:

For the items, I have assigned classes to the prev and next buttons instead of IDs.

<div class="item" id="1">
    <img src="http://www.leecorbin.co/img1.jpg" width="50%" />
    <div class="caption">caption 1</div>
    <div class="navigation"> 
        <a href="#" class="prevBtn">&lt</a> 
        1 / 3 
        <a href="#" class="nextBtn">&gt</a>
    </div>
</div>

Then in script, on pagecreate

Hide all items and show only the first one. Add a handler for swipeleft and swiperight on items. Add a click handler for the navigation buttons Within these handlers determine which direction we are going and which slide we are currently on. Call a function passing in the direction and current slide; it determines the next slide to show and makes the transition.

$(document).on("pagecreate", "#page1", function () {
    $(".item").hide().first(0).show();

    $(document).on("swipeleft swiperight", ".item", function (e) {
        var dir = 'prev';
        if (e.type == 'swipeleft') {
            dir = 'next';
        }
        GoToNextSlide($(this), dir);
    });

    $(document).on("click", ".navigation > a", function (e) {
        var dir = 'prev';
        if ($(this).hasClass("nextBtn")) {
            dir = 'next';
        }
        var $item = $(this).closest(".item");
        GoToNextSlide($item, dir);
    });

});

function GoToNextSlide($item, direction) {
    var $next;
    if (direction == 'next') {
        if ($item.next().length > 0) {
            $next = $item.next();
        } else {
            $next = $(".item").first();
        }
    } else {
        if ($item.prev().length > 0) {
            $next = $item.prev();
        } else {
            $next = $(".item").last();
        }
    }
    $item.fadeOut(function () {
        $next.fadeIn();
    });
}

Updated DEMO

animuson
  • 53,861
  • 28
  • 137
  • 147
ezanker
  • 24,628
  • 1
  • 20
  • 35
  • I think your code works well. Now I have to make it cooperate with [this function](http://jsfiddle.net/admiringtheorchid/2hgwx8Lg/19/) that responsively centers the image on the page. [Here is your demo with a few adjustments.](http://jsfiddle.net/admiringtheorchid/ej40c8m7/8/) How can I make it work with the additional script? – D Benway Mar 13 '15 at 02:23
  • Adding to my last comment – [I added the script that centers the image to my first demo](http://jsfiddle.net/admiringtheorchid/pfe2n05p/2/) and found that while it is still dysfunctional (only the first image in the array is centered, the others appear at the bottom of the page) it works better than when it is added to your solution. I'm hoping there's a clue here. – D Benway Mar 13 '15 at 02:48
  • @DBenway, here you go: http://jsfiddle.net/ezanker/ej40c8m7/11/ With jQM use events like pagecreate instead of document.ready and window load. Also, because the other images are hidden at the time doresize is called, the calculation fails for all but the first item. instead use CSS like here: http://zerosixthree.se/vertical-align-anything-with-just-3-lines-of-css/ – ezanker Mar 13 '15 at 14:24
  • Thank you sincerely @ezanker. [The updated fiddle](http://jsfiddle.net/admiringtheorchid/ej40c8m7/12/) works well on an iPhone 5 and in FF, Chrome and Safari after adding -webkit-transform: translateY(-50%); to div.item > img It is buggy in IE10 from what I suspect to be a problem with the CSS. Something I find undesirable is that while a swiping gesture on a touchscreen laptop navigates the array, it also selects the image and all of the text on the page. How might I prevent it from doing so? – D Benway Mar 14 '15 at 02:07
  • I would also like to make it possible to navigate the array by simply touching (in addition to swiping) the left- or right-hand portions of the screen. How might I achieve this? – D Benway Mar 14 '15 at 02:07
  • @DBenway, try adding this CSS and draggable="false" to the images: http://jsfiddle.net/ezanker/ej40c8m7/13/ – ezanker Mar 14 '15 at 14:15
  • @DBenway and for touching near the edge you can use the e.pageX to see if you are closer to the edge: http://jsfiddle.net/ezanker/ej40c8m7/14/ – ezanker Mar 14 '15 at 16:44
  • it's nearly there. draggable="false" seems to be working. e.pageX is problematic because it targets the edge of the page when it is the entire left- or right-hand half of the page that I wish to make active. I tried setting the xCoord to 50% without success. Using tap on the image allows a visitor to advance through the array. I'd like to find a way for them to go back as well. Is there another solution? http://jsfiddle.net/admiringtheorchid/ej40c8m7/15/ – D Benway Mar 14 '15 at 18:29
  • @DBenway, 50% point works fine for me: http://jsfiddle.net/ezanker/ej40c8m7/16/ if you want to use touchstart instead of click, it would be e.originalEvent.pageX – ezanker Mar 15 '15 at 01:46
  • Thanks for updating the fiddle @ezanker, e.pageX now works very well. My iphone 5 wasn't responding to the click event when tapping the screen. I replaced it with tap successfully. Is touchstart superior to tap? I encounterd a bug in chrome where the tap event fired twice every time it was triggered. I found [a solution here](http://stackoverflow.com/questions/10794181/jquery-mobile-tap-event-triggered-for-twice) and added e.preventDefault(); after all actions in $(document).on("tap", ".item", function(e) {}). Now it works as expected: http://jsfiddle.net/admiringtheorchid/ej40c8m7/17/ – D Benway Mar 16 '15 at 00:06
  • This discussion has been very instructive @ezanker, thank you again. – D Benway Mar 16 '15 at 04:34
  • Reviewing http://jsfiddle.net/ej40c8m7/18/ it occurred to me that since I am now using transform: translateY to vertically center the image, the DoResize function is redundant. However, when I remove it from the code, div.item is rendered far above the top of the screen. Why is that @ezanker? – D Benway Mar 20 '15 at 21:16
  • Setting item height to 100% in CSS only works if its container has a height:. You can scale the content div to fill the page, then your CSS will work: http://jsfiddle.net/ezanker/ej40c8m7/19/ – ezanker Mar 20 '15 at 21:31
  • Can't the height of the container be set to 100% in CSS? – D Benway Mar 20 '15 at 23:06
  • @DBenway as long as you account for header and footer, etc. See this answer for several ways to do it: http://stackoverflow.com/questions/21552308/set-content-height-100-jquery-mobile – ezanker Mar 22 '15 at 14:46
  • I am trying to bind a mouseover event to the XCoord function to render the cursor as a left- or right-pointing arrow depending on its location. I included the code beneath the function that calls the next or previous slide. In this demo: http://jsfiddle.net/ej40c8m7/21/ the cursor must pass beyond half of the page before the cursor image changes which is confusing and it reverts to the traditional cursor on click. Do you know why @ezanker? – D Benway Mar 29 '15 at 16:28
  • @DBenway, see if the mousemove event works better than mouseover: http://jsfiddle.net/ej40c8m7/22/ – ezanker Mar 29 '15 at 22:33
  • Thanks @ezanker, mousemove is an improvement. Where it fails is when the visitor clicks the mouse at which point the cursor reverts to the default or, worse, the opposite facing arrow. I assume this is because the mouse must be in motion for the function to work. Is there a way to time the function so that the image remains after the mouse is clicked? – D Benway Mar 31 '15 at 17:19
  • @DBenway, what if you trigger the mousemove event on click: $(this).trigger("mousemove"); http://jsfiddle.net/ezanker/ej40c8m7/24/ – ezanker Mar 31 '15 at 18:24
  • Triggering mousemove on click did not solve the problem. I divided the window into two divs, "left" and "right", then used the mouseenter function to render the cursor. While this works, it overrides the click function on .item which advances or recedes the slideshow. I added the js: (document).on("click", ".left", function(e) { GoToNextSlide($item, 'prev'); }); $(document).on("click", ".right", function(e) { GoToNextSlide($item, 'next'); }); without success: http://jsfiddle.net/ej40c8m7/28/ What do you suggest @ezanker? – D Benway Apr 01 '15 at 20:20
  • @DBenway, $item is meaningless in the .left and .right click. You have to get the visible .item: http://jsfiddle.net/ezanker/ej40c8m7/29/ – ezanker Apr 01 '15 at 20:41
  • That works perfectly @ezanker. I need to call the swipe and tap functions on the class 'area' ('left' and 'right' are the id's) since 'item' sits beneath it. This works fine in Chrome but in Firefox the swipe gesture is triggered at the start and end of a swipe so that each swipe advances two images. Have I written the code wrong? Is there a way to set a threshold on the length of a swipe or trigger an event only on the start of a swipe? http://jsfiddle.net/ej40c8m7/54/ – D Benway Apr 03 '15 at 22:05
  • @DBenway, when you swipe and let go within the same .area div, both the swipe and the tap are being triggered. – ezanker Apr 04 '15 at 19:45
  • Why doesn't the same problem occur here? [ $(document).on("swipeleft swiperight", ".item", function (e) { var dir = 'prev'; if (e.type == 'swipeleft') { dir = 'next'; } GoToNextSlide($(this), dir); }); ] [ $(document).on("tap", ".item", function(e) { var xCoord = e.pageX; var halfW = $(this).width() / 2; if (xCoord < halfW){ GoToNextSlide($(this), 'prev'); } else { GoToNextSlide($(this), 'next'); } e.preventDefault(); }); ] jsfiddle.net/admiringtheorchid/ej40c8m7/17 – D Benway Apr 05 '15 at 03:24
  • I suspect the problem has something to do with the line [ var $item = $(".item:visible"); ] in [ $(document).on("swipeleft swiperight", ".area", function (e) { ] and [ $(document).on("tap", ".area", function(e) { ] since it differs between the two examples. – D Benway Apr 05 '15 at 16:20
  • How can I prevent tap from being triggered on swipe within .area div @ezanker? http://jsfiddle.net/ej40c8m7/60/ – D Benway Apr 09 '15 at 21:08
  • Thank you for taking me as far as you did @ezanker. – D Benway Apr 13 '15 at 01:38