jsor / jcarousel

Riding carousels with jQuery.
https://sorgalla.com/jcarousel/
MIT License
2k stars 736 forks source link

Abilities of the 0.3 branch #352

Closed Dupond closed 12 years ago

Dupond commented 12 years ago

I'd like to convert my carousels to 0.3 branch, in order to fix my focus problems, and the resize problems under Chrome, as discussed in several issues here.

Is there some kind of know limitations with this branch, compared to the 0.2.8 one ? For example :

Furthermore, I've put my config code in an external file : should I use $(document).ready() (as I used to with 0.2.8) ? For the moment, I just have the following code in my config file :

$(function() { $('#carousel-editos').jcarousel()
    .find('[tabindex]')
    .focus(function(e) { e.preventDefault(); e.stopPropagation(); $(this).closest('.jcarousel').jcarousel('scroll', this); });
});

Is that fine ?

Thanx for your help, Dupond

jsor commented 12 years ago

This

$(function() {
});

is the same as

$(document).ready(function() {
});

So, all fine :)

There are no built in item loading related callbacks in 0.3. This has to be done in userland code. A simple loading would be:

$.get('/url/to/items.xml', function(data) {
    $(data).find('item').each(function() {
        // Create <li> element and append it to the list
    });
    // Reload (or create) carousel
    $('#carousel').jcarousel('reload');
}, 'xml');
Dupond commented 12 years ago

OK, it works fine !

There is still a problem though : I'd like to load ONLY the images that are visible in the carousel. With the 0.2.8 version, the following code worked :

function carousel_comments_itemLoadCallback(carousel, state) {
    for (x=carousel.first-1;x<carousel.last;x++) {
        // the code to load the image
    }
}

How could I change "itemLoadCallback()", "carousel.first" and "carousel.last" ?

Furthermore, I was using "itemVisibleInCallback" and "carousel.size()" to add an "inactive" class to the external control links :

function carousel_editos_itemVisibleInCallback(carousel, item, idx, state) {
    if (carousel.first == 1) { $("#editos .jcarousel-prev").css("visibility", "hidden"); } else { $("#editos .jcarousel-prev").css("visibility", "visible"); }
    if (carousel.last == carousel.size()) { $("#editos .jcarousel-next").css("visibility", "hidden"); } else { $("#editos .jcarousel-next").css("visibility", "visible"); }
}

Which callback could I use instead of it ? How can I replace carousel.size() too ?

NB : it can be seen on this page.

Thanx for your help, Best regards, Dupond

jsor commented 12 years ago

There is no itemLoadCallback. You have to do everything by yourself. I will add an example, but for now you're on your own ;)

There are some helper methods you can use for that:

carousel.first() returns the first visible item carousel.first().index() returns the position (0-based) carousel.last() returns the last visible item carousel.last().index() returns the position (0-based) carousel.visible()returns all visible items carousel.list()returns all items carousel.list().size() returns the number of items

Dupond commented 12 years ago

I'm afraid I won't be able to do this on my own :( I understand there is no built-in callback. But I don't know how to replace them by myself.

So I think I'll wait for your example ;)

charleskline commented 12 years ago

I don't see this helper method of list() in 0.3 branch. Am I missing something?

var jc = $("#my-carousel").jcarousel();

jc.list();

TypeError: Object [object Object] has no method 'list'

jsor commented 12 years ago

You have to call the method in the carousel object, not the jQuery object.

jc.jcarousel('list');

or

jc.data('jcarousel').list();
Dupond commented 12 years ago

I think I could emulate the itemVisibleInCallback and itemLoadCallback, creating my own itemLoadCallback function with this code :

$(function() {
    $("#my-carousel").jcarousel().load(function() { itemLoadCallback(carousel); });
    $("#prev-link").click(function() { itemLoadCallback(carousel); });
    $("#next-link").click(function() { itemLoadCallback(carousel); });
});

function itemLoadCallback(carousel) {
    // my code goes here;
}

The aim is to have a function that would be triggered when the carousel loads for the first time, and, then, each time a new set of items is requested. Is it the right way to achieve this ?

jsor commented 12 years ago

Nope, read the .load() docs.

Dupond commented 12 years ago

OK, I'm far to be a pro... I guess I'll just wait for your example.

Dupond commented 12 years ago

A few more questions please.

  1. Can you tell me where I will be able to find your example when it is ready ? Will it be there on Github ? Or did you mean on this thread ?
  2. I used to put the links to jquery.js and jcarousel.js at the end of my html file (just before </body>). But if I do this now, it simply doesn't work anymore. Do you know why ?
  3. I have an error in the console when I click on an external control link which is set like this :
$("#pager-button-prev a").click(function() { $("#carousel-comments").jcarousel("scroll", "-=1"); return false; });

Error : this._first is null first = this._first.index(); jquery.jcarousel.js (line 580)

And so the external control link doesn't work.

  1. Is it possible to scroll horizontally while displaying 3 items vertically in the carousel ? I used to have this code :
<ul>
    <li>
        <ul>
            <li>item 1</li>
            <li>item 2</li>
            <li>item 3</li>
        </ul>
    </li>
    <li>
        <ul>
            <li>item 4</li>
            etc...
        </ul>
    </li>
</ul>

But since 0.3, it doesn't seem to work anymore (it's the 2nd carousel on the top right of this page, called "Nos prochaines animations"). It only displays one item at a time, instead of 3. Is it because of a change in the jCarousel code ? Or is it just a CSS problem ?

Thanx, Best regards, Dupond

jsor commented 12 years ago
  1. I've added a loader plugin in the loader branch: https://github.com/jsor/jcarousel/tree/loader/plugins/loader
  2. No idea, theoretically it should work.
  3. Thats because you add the <ul></ul> after you initialize the carousel (in carousel_comments_init).
  4. If you have nested lists, you shluld use the listoption to only select the first level <ul>:

    $('#carousel').jcarousel({
       list: '>ul'
    });
Dupond commented 12 years ago

Thanx for your plugin !

There is a mistake in your example code here : you have put a "," instead of a ";" in the following line : for (var i = first; i <= last, i++) {

The images are displayed thanx to your plugin, but it doesn't seem to work as expected : here is my code now :

img = new Array(Array);
$(function() {
    $("#carousel-comments-container").append('<div class="jcarousel" id="carousel-comments"><ul>\n</ul></div>');
    var loadCallback = function(first, last, type, ready) {
        $.get('../dotclear-files/themes/biblio/js/dynamic_ajax_php.php', function(data) {
            var w = 0;
            $(data).find('image').each(function() {
                if (!img[w]) img[w] = new Array();
                img[w]['url'] = $(this).find('url').text();
                img[w]['src'] = $(this).find('src').text();
                img[w]['title'] = $(this).find('title').text();
                img[w]['width'] = $(this).find('width').text();
                img[w]['height'] = $(this).find('height').text();
                w++;
            });
            var items = $('#carousel-comments').jcarousel('items');
            for (var i = first; i <= last; i++) {
                items.eq(i).find('img').attr('src', img[i]['src']);
            };
            ready();
        }, 'xml');
    };
    var placeholderCallback = function(index) {
        return '<li><img src="../dotclear-files/themes/biblio/img/loading.gif"></li>';
    };
    $('#carousel-comments')
        .jcarousel()
        .jcarouselLoader({
            load: loadCallback,
            placeholder: placeholderCallback
        });
    $("#pager-button-prev a").click(function() { $("#carousel-comments").jcarousel("scroll", "-=3"); return false; });
    $("#pager-button-next a").click(function() { $("#carousel-comments").jcarousel("scroll", "+=3"); return false; });
});
  1. This code seems to load the xml file for all the items each time you scroll... Is there a way to load it only once ?
  2. The images are already loaded when scrolling to the next items... I had the following piece of code, so that the images are only displayed when they're loaded : should I include it somewhere ?

    $('#jcarousel-img-' + item) .hide() .one('load', function() { $(this).fadeIn(); }) .attr('src', img[item]['src']) .each(function() { if (this.complete) $(this).trigger('load'); });

  3. The carousel never stops ! You can scroll to the next items again and again, even if there is no more images to load...
  4. Could we just disable the prev/next buttons when the carousel is at its first/last position ?

Thanx for your great help !

jsor commented 12 years ago

Try this (not tested by me):

$(function() {
    var loadCallback = function(first, last, type, ready) {
        // Only load once on initialisation
        if (type !== 'init') {
            return;
        }

        var plugin   = this,
            carousel = this.carousel(),
            list     = carousel.list(),
            items    = carousel.items();

        $.get('../dotclear-files/themes/biblio/js/dynamic_ajax_php.php', function(data) {
            // Set size
            plugin.option('size', parseInt($(data).find('total').text(), 10));

            // Add items
            $(data).find('image').each(function(index) {
                // Create the actual item
                var item = $('<li><a href="' + $(this).find('url').text() + '"><img src="' + $(this).find('src').text() + '"></a></li>');

                // Check if there is a placeholder item at this position
                var existing = items.eq(index);

                if (existing.size() > 0) {
                    // Replace placeholder
                    existing.replaceWith(item);
                } else {
                    // Append new item
                    list.append(item);
                }
            });

            ready();
        }, 'xml');
    };

    var placeholderCallback = function(index) {
        return '<li></li>';
    };

    var carouselComments = $('<div class="jcarousel" id="carousel-comments"><ul></ul></div>')
        .appendTo('#carousel-comments-container')
        .jcarousel()
        .jcarouselLoader({
            load: loadCallback,
            placeholder: placeholderCallback
        });

    $("#pager-button-prev a").click(function() { carouselComments.jcarousel("scroll", "-=1"); return false; });
    $("#pager-button-next a").click(function() { carouselComments.jcarousel("scroll", "+=1"); return false; });
});
Dupond commented 12 years ago

Thanx again for your help.

  1. The "size" limit isn't set properly, it remains "10" instead of the "total" value indicated in my xml file...
  2. The images are still loaded before I scroll (they are displayed instantaneously, instead of loading a preload.gif animation).
  3. Only two images are scrolled, instead of 3 (my prev/next buttons are configured with "-=3" and "+=3" values)...
jsor commented 12 years ago
  1. The option is set correctly. But i discovered a bug in the plugin which should probably also requires some changes in the jCarousel core. Will try to fix this asap.
  2. I havent't included this, you should apply the logic to the created item here:

    var item = $('<li><a href="' + $(this).find('url').text() + '"><img src="' + $(this).find('src').text() + '"></a></li>');
  3. Same as 1.
Dupond commented 12 years ago

OK, I think I've got it, but I'm not sure : it behaves strangely, but maybe it's just because of the bug you talked about ?

My images still don't seem to be loaded only when scrolling though... It still seems that they're all loaded at once when the carousel loads for the first time... I've tried to "apply the logic" as you said :

var img = new Array(Array);
$(function() {
    var loadCallback = function(first, last, type, ready) {
        var plugin   = this,
            carousel = this.carousel();
        if (type !== 'init') { carousel_comments_itemLoad(carousel, first, last); return; }
        $.get('../dotclear-files/themes/biblio/js/dynamic_ajax_php.php', function(data) {
            plugin.option('size', parseInt($(data).find('total').text(), 10));
            $(data).find('image').each(function(index) {
                if (!img[index]) img[index] = new Array();
                img[index]['url'] = $(this).find('url').text();
                img[index]['src'] = $(this).find('src').text();
                img[index]['title'] = $(this).find('title').text();
                img[index]['width'] = $(this).find('width').text();
                img[index]['height'] = $(this).find('height').text();
            });
            ready();
            carousel_comments_itemLoad(carousel, first, last);
        }, 'xml');
    };
    var placeholderCallback = function(index) { return '<li></li>'; };
    var carouselComments = $('<div class="jcarousel" id="carousel-comments"><ul>\n</ul></div>')
        .appendTo('#carousel-comments-container')
        .jcarousel()
        .jcarouselLoader({ load: loadCallback, placeholder: placeholderCallback });
    $("#pager-button-prev a").click(function() { $("#carousel-comments").jcarousel("scroll", "-=3"); return false; });
    $("#pager-button-next a").click(function() { $("#carousel-comments").jcarousel("scroll", "+=3"); return false; });
});

function carousel_comments_itemLoad(carousel, first, last) {
    var list  = carousel.list(),
        items = carousel.items();
    for (x=first; x<=last; x++) {
        var item = $('<li><a class="black-links" href="' + img[x]['url'] + '#comments" title="' + img[x]['title'] + '"><img src="../dotclear-files/themes/biblio/img/loading.gif" alt="' + img[x]['title'] + '" id="carousel-img-' + x + '" width="' + img[x]['width'] + 'px" height="' + img[x]['height'] + 'px" /></a></li>');
        var existing = items.eq(x);
        if (existing.size() > 0) { existing.replaceWith(item); } else { list.append(item); }
        $('#carousel-img-' + x)
            .hide()
            .one('load', function() { $(this).fadeIn(); })
            .attr('src', img[x]['src'])
            .each(function() { if (this.complete) $(this).trigger('load'); });
    }
}
jsor commented 12 years ago

While writing the loader plugin, i realized that it not really adds some value, so i'm going to remove it ;)

Here's a reworked version of the code using a core event and including the fade in stuf of the images.

Note: The images are loaded all at once on initialization. Is there any reason why you don't want to do that?

$(function() {
    var loadCallback = function(event, carousel) {
        var list  = carousel.list();

        $.get('../dotclear-files/themes/biblio/js/dynamic_ajax_php.php', function(data) {
            $(data).find('image').each(function(index) {
                // Create the actual item
                var item = $('<li><a class="black-links" href="' + $(this).find('url').text() + '#comments" title="' + $(this).find('title').text() + '"><img src="../dotclear-files/themes/biblio/img/loading.gif" width="' + $(this).find('width').text() + '" height="' + $(this).find('height').text() + '"></a></li>');

                list.append(item);

                item.find('img')
                    .hide()
                    .one('load', function() { $(this).fadeIn(); })
                    .attr('src', $(this).find('src').text())
                    .each(function() { if (this.complete) $(this).trigger('load'); });
            });

            carousel.reload();
        }, 'xml');
    };

    var carouselComments = $('<div class="jcarousel" id="carousel-comments"><ul></ul></div>')
        .appendTo('#carousel-comments-container')
        .bind('jcarouselinit', loadCallback)
        .jcarousel();

    $("#pager-button-prev a").click(function() { carouselComments.jcarousel("scroll", "-=1"); return false; });
    $("#pager-button-next a").click(function() { carouselComments.jcarousel("scroll", "+=1"); return false; });
});
Dupond commented 12 years ago

It's because if you have 100 images, loaded all at once on initialization, it'll be veeeeery long to load ;) So what would be nice is to load the xml code from the PHP file once and for all on initialization, and then only load the images themselves when they need to be displayed...

Dupond commented 12 years ago

Furthermore, if the user doesn't scroll, you'll have loaded 97 images for nothing...

I'm a bit baffled here : is it just not possible with the 0.3 branch ? You had done it yourself quite easily in your own example for 0.2.8, using itemLoadCallback (and it worked quite well) : so we would just need an equivalent to this in the 0.3 branch.

jsor commented 12 years ago

The item loading stuff in 0.2 is flawed. It worked well only in few cases.

If you want to load the images only if the become visible, just do this

    $.get('../dotclear-files/themes/biblio/js/dynamic_ajax_php.php', function(data) {
        $(data).find('image').each(function(index) {
            // Create the actual item
            var item = $('<li><a class="black-links" href="' + $(this).find('url').text() + '#comments" title="' + $(this).find('title').text() + '"><img data-src="' + $(this).find('src').text() + ' src="../dotclear-files/themes/biblio/img/loading.gif" width="' + $(this).find('width').text() + '" height="' + $(this).find('height').text() + '"></a></li>');

            list.append(item);

            item.bind('jcarouselitemvisiblein' function() {
                var img = $(this).find('img'),
                    src = img.data('src');

                if (!src) {
                    return;
                }

                img
                    .hide()
                    .one('load', function() { $(this).fadeIn(); })
                    .attr('src', src)
                    .each(function() { if (this.complete) $(this).trigger('load'); });
            });
        });

        carousel.reload();
    }, 'xml');

We simply bind on the jcarouselitemvisiblein event and if it's triggered, we exchange the src attribute to the real image url stored in the data-src attribute

Dupond commented 12 years ago

Thank you soooo much for your great help ! (back from holidays maybe ? ;)

I'm not sure the method is yet perfect though (you'll clearly see the problem if you browse without CSS) : with this code (which works just fine, as we can see on the page where I use it), all the images are added at once, but only 3 are indeed visible. So if you browse with a screen reader for example (for people with disabilities etc.), you have useless elements displayed (that is to say : you have a list of loading gifs !)... So it would be better to load the PHP file, and, then, to only add to the DOM the images that are visible, don't you think so ? Is it possible to achieve this ?

Furthermore, the width and height attributes of the images should be added only after the images are loaded (because the loading gif is displayed much bigger than it should).

By the way, one of my carousel (still on my page, it's the one called "Nos prochaines animations") simply doesn't work with 0.3 : you had advised me (12th comment of this post) to add $("#carousel-lastevents").jcarousel({animation: 0, list: '>ul'}); so that's what I've done : the items are displayed, but the scroll doesn't work (the "next" button has no effect indeed).

Any idea anyone ? Thanx !

jsor commented 12 years ago

Updated code which only adds required items (without loader plugin), untested as usual ;):

$(function() {
    var carouselCommentsNumItems = 3, // 3 items visible
        carouselCommentsData = [];

    var createItem = function(data) {
        var item = $('<li><a class="black-links" href="' + data.url + '#comments" title="' + data.title + '"><img data-src="' + data.src + ' src="../dotclear-files/themes/biblio/img/loading.gif"></a></li>');

        var img = item.find('img');

        img
            .hide()
            .one('load', function() { $(this).attr('width', data.width).attr('height', data.height).fadeIn(); })
            .attr('src', src)
            .each(function() { if (this.complete) $(this).trigger('load'); });
    };

    var initCallback = function(event, carousel) {
        var list  = carousel.list();
        $.get('../dotclear-files/themes/biblio/js/dynamic_ajax_php.php', function(data) {
            // Store data
            $(data).find('image').each(function(index) {
                var item = {
                    'url': $(this).find('url').text(),
                    'title': $(this).find('title').text(),
                    'src': $(this).find('src').text(),
                    'width': $(this).find('width').text(),
                    'height': $(this).find('height').text()
                };

                carouselCommentsData.push(item);
            });

            // Create first set of items
            var list  = carousel.list(),
                size = carouselCommentsData.length;

            for (var i = 0, i < carouselCommentsNumItems; i++) {
                if (i >= size) {
                    break;
                }

                list.append(createItem(carouselCommentsData[i]));
            }

            carousel.reload();
        }, 'xml');
    };

    var scrollCallback = function(event, carousel, target) {
        var list  = carousel.list(),
            items = carousel.items(),
            size = carouselCommentsData.length,
            parsed = jCarousel.parseTarget(target),
            index = parsed.target;

        // Only relative (prev/next buttons)
        if (!parsed.relative) {
            return;
        }

        // Out of range
        if (index >= size) {
            return;
        }

        // Already added
        if (items.eq(index).size() > 0) {
            return;
        }

        for (var i = index, i < carouselCommentsNumItems; i++) {
            if (i >= size) {
                break;
            }

            list.append(createItem(carouselCommentsData[i]));
        }

        carousel.reload();
    };

    var carouselComments = $('<div class="jcarousel" id="carousel-comments"><ul></ul></div>')
        .appendTo('#carousel-comments-container')
        .bind('jcarouselinit', initCallback)
        .bind('jcarouselscroll', scrollCallback)
        .jcarousel();

    $("#pager-button-prev a").click(function() { carouselComments.jcarousel("scroll", "-=1"); return false; });
    $("#pager-button-next a").click(function() { carouselComments.jcarousel("scroll", "+=1"); return false; });
});

Regarding the not-working carousel, try:

$("#carousel-lastevents").jcarousel({animation: 0, list: '>ul', items: '>li});
Dupond commented 12 years ago

Wow ! Thanx a lot ! But unfortunately it doesn't seem to work : I've changed a few typos ("," instead of ";" in the "for" loop, for example, or "src" instead of "data.src" on line 13), and I've put it online : but, as you can see, it does nothing. The images are not displayed (in fact, the items are not added to the DOM, as we can see in the generated source code).

Here is the final code I used :

$(function() {
    $("#carousel-comments-container").prepend('\n<span id="pager-button-prev"><a class="black-links" href="#" title="Documents pr&eacute;c&eacute;dents">&lt;</a></span>\n<span id="pager-button-next"><a class="black-links" href="#" title="Documents suivants">&gt;</a></span>');
    var carouselCommentsNumItems = 3, // 3 items visible
        carouselCommentsData = [];

    var createItem = function(data) {
        var item = $('<li><a class="black-links" href="' + data.url + '#comments" title="' + data.title + '"><img data-src="' + data.src + '" src="../dotclear-files/themes/biblio/img/loading.gif"></a></li>');

        var img = item.find('img');

        img
            .hide()
            .one('load', function() { $(this).attr('width', data.width).attr('height', data.height).fadeIn(); })
            .attr('src', data.src)
            .each(function() { if (this.complete) $(this).trigger('load'); });
    };

    var initCallback = function(event, carousel) {
        var list  = carousel.list();
        $.get('../dotclear-files/themes/biblio/js/dynamic_ajax_php.php', function(data) {
            // Store data
            $(data).find('image').each(function(index) {
                var item = {
                    'url': $(this).find('url').text(),
                    'title': $(this).find('title').text(),
                    'src': $(this).find('src').text(),
                    'width': $(this).find('width').text(),
                    'height': $(this).find('height').text()
                };

                carouselCommentsData.push(item);
            });

            // Create first set of items
            var list  = carousel.list(),
                size = carouselCommentsData.length;

            for (var i = 0; i < carouselCommentsNumItems; i++) {
                if (i >= size) {
                    break;
                }

                list.append(createItem(carouselCommentsData[i]));
            }

            carousel.reload();
        }, 'xml');
    };

    var scrollCallback = function(event, carousel, target) {
        var list  = carousel.list(),
            items = carousel.items(),
            size = carouselCommentsData.length,
            parsed = jCarousel.parseTarget(target),
            index = parsed.target;

        // Only relative (prev/next buttons)
        if (!parsed.relative) {
            return;
        }

        // Out of range
        if (index >= size) {
            return;
        }

        // Already added
        if (items.eq(index).size() > 0) {
            return;
        }

        for (var i = index; i < carouselCommentsNumItems; i++) {
            if (i >= size) {
                break;
            }

            list.append(createItem(carouselCommentsData[i]));
        }

        carousel.reload();
    };

    var carouselComments = $('<div class="jcarousel" id="carousel-comments"><ul></ul></div>')
        .appendTo('#carousel-comments-container')
        .bind('jcarouselinit', initCallback)
        .bind('jcarouselscroll', scrollCallback)
        .jcarousel();
    $("#pager-button-prev a").bind('jcarouselcontrolinactive', function() { $("#pager-button-prev").css("visibility", "hidden"); }).bind('jcarouselcontrolactive', function() { $("#pager-button-prev").css("visibility", "visible"); }).jcarouselControl({ target: '-=3' });
    $("#pager-button-next a").bind('jcarouselcontrolinactive', function() { $("#pager-button-next").css("visibility", "hidden"); }).bind('jcarouselcontrolactive', function() { $("#pager-button-next").css("visibility", "visible"); }).jcarouselControl({ target: '+=3' });
});

(However, regarding the previously not-working carousel, now it works ! Thanx a lot !)

jsor commented 12 years ago

The createItem function misses the return statement ;-)

var createItem = function(data) {
    var item = $('<li><a class="black-links" href="' + data.url + '#comments" title="' + data.title + '"><img data-src="' + data.src + '" src="../dotclear-files/themes/biblio/img/loading.gif"></a></li>');

    var img = item.find('img');

    img
        .hide()
        .one('load', function() { $(this).attr('width', data.width).attr('height', data.height).fadeIn(); })
        .attr('src', data.src)
        .each(function() { if (this.complete) $(this).trigger('load'); });

    return item;
};
Dupond commented 12 years ago

OK, the items are added now... but there is no more prev/next scroll button added ;)

Should I change a particular variable ? Thanx very much for all your help ;)

jsor commented 12 years ago

Now, that we load the items dynamically, the prev/next buttons don't know that there are more items available than actually items in the carousel.

I've just pushed to the master branch. Take jquery.jcarousel.js from there and change the following section:

var carouselComments = $('<div class="jcarousel" id="carousel-comments"><ul></ul></div>')
    .appendTo('#carousel-comments-container')
    .bind('jcarouselinit', initCallback)
    .bind('jcarouselscroll', scrollCallback)
    .bind('jcarouselhasnext', function(event, carousel) {
        if (carousel.last().index() < carouselCommentsData.length) {
            event.preventDefault();
        }
    })
    .jcarousel();

The new part is the binding to the jcarouselhasnextevent.

Dupond commented 12 years ago

Thanx very much ! Unfortunately, it doesn't seem to work : as you can see, there is still no prev/next scroll button displayed... Maybe I've done something wrong ?

jsor commented 12 years ago

You must use the latest version of jquery.jcarousel.js: https://github.com/jsor/jcarousel/blob/master/src/jquery.jcarousel.js

Dupond commented 12 years ago

Yes, that's what I've done. Just to be sure, I just come to do it again : same result. (I click on the link you gave, and then I copy/paste the code to my jquery.jcarousel.js file, since I don't find how to download it)...

jsor commented 12 years ago

I checked the file /dotclear-files/themes/biblioTest/js/min/jquery.jcarousel.js and it doesn't have the latest changes in it (b1e0f6ff097fcb3caa3de617ec26a016dd52df8b).

Dupond commented 12 years ago

Grrr... Sorry for this : I had changed the wrong file.

Now it's better (the "next" scroll button is displayed) but not yet perfect : the next scroll button has no effect (that is to say : when you click on it, nothing happens... it just doesn't scroll)...

Thanx very much for all the help you give to me !

jsor commented 12 years ago

We have a javascript error: carousel.last() is null. Change the new callback to:

.bind('jcarouselhasnext', function(event, carousel) {
    var last = carousel.last();
    if (last && last.index() < carouselCommentsData.length) {
        event.preventDefault();
    }
})
Dupond commented 12 years ago

Done... but, unfortunately, same behaviour : the scroll button still doesn't seem to work...

jsor commented 12 years ago

Small bug. Replace:

for (var i = index; i < carouselCommentsNumItems; i++) {

with

for (var i = index; i < i + carouselCommentsNumItems; i++) {
Dupond commented 12 years ago

Thanx ! Now it works just fine ! The "next" scroll buttons never "disappear" though (I'm using the "control" plugin, and it never becomes inactive). This is strange, since the "prev" button works just fine...

Anyway, thank you very much for all the time you gave to this project, since I'd never have been able to do this on my own !

Here are a few more things I noticed :

  1. Is the data-src=etc. attribute of the image still needed ? I was under the impression that it wasn't useful anymore.
  2. Isn't the "list" variable declared twice in the initCallback function (where we can find two var list = carousel.list()) ?
  3. Sorry if this seems obvious for you : is there any advantage to write var list=carousel.list(); list.append(etc.) (2 lines of code) instead of carousel.list().append(etc...) (only 1 line of code) ?
  4. Since an image is already an inline-element, why is there a style="display: inline;" attribute added to the images ?
  5. Wouldn't it make sense to add a callback to jCarousel which would be triggered at initialization of the carousel AND at scrolling (that is to say, each time a new set of items is requested) ? That way, you wouldn't have to write redundant code (the for (var i=etc...) loop, for example). Furthermore, creating a counter would be much easier : something like $("#editos .counter").text("").prepend(carousel.first().index() + " / " + carousel.list().size()); that we could put in the correct callback ;)
jsor commented 12 years ago

Fix for the next button:

Replace

if (last && last.index() < carouselCommentsData.length) {

with

if (last && last.index() < (carouselCommentsData.length - 1)) {

Question 1- 4: Yep, i just wrote that code out of my head. There is probably room for improvement ;-) Question 5: You can bind to 2 events at once: $().bind('jcarouselinit,jcarouselscroll', function() {});

Dupond commented 12 years ago

Mmmm... Not sure the "-1" should be added there, since when you scroll for the first time, 4 items are added at once (instead of three : cf. var carouselCommentsNumItems = 3, // 3 items visible).

As for the addition of style="display: inline;" on the images, it seems to be added by jCarousel itself, since it doesn't appear in the js config file...

jsor commented 12 years ago

The scrollCallback is buggy, replace with

var scrollCallback = function(event, carousel, target) {
    var list  = carousel.list(),
        items = carousel.items(),
        size = carouselCommentsData.length,
        parsed = jCarousel.parseTarget(target),
        first = carousel.last().index() + 1,
        last = first + parsed.target;

    for (var i = first; i < last; i++) {
        if (i >= size) { break; }
        list.append(createItem(carouselCommentsData[i]));
    }
    carousel.reload();
};

Don't know why style="display: inline;"is added, definitely not from jCarousel. Probably it is added by jQuery from .hide()/.fadeIn().

Dupond commented 12 years ago

It works... only the first time you scroll : scroll ("next" button) twice (until the end of the carousel, in fact), then scroll back ("prev" button once or twice, it doesn't matter), and then scroll again ("next" button again) : the four last items are added several times (each time you scroll back and forth, they're added again)...

jsor commented 12 years ago

Ah, yes. forgt to add the check if the item already exists:

var scrollCallback = function(event, carousel, target) {
    var list  = carousel.list(),
        items = carousel.items(),
        size = carouselCommentsData.length,
        parsed = jCarousel.parseTarget(target),
        first = carousel.last().index() + 1,
        last = first + parsed.target;

    for (var i = first; i < last; i++) {
        if (i >= size) { break; }
        if (items.eq(i).size() > 0) { continue; }
        list.append(createItem(carouselCommentsData[i]));
    }
    carousel.reload();
};
Dupond commented 12 years ago

Aaaahhh ! Now it really works like a charm :)

I'm very grateful to you of all the time you gave to me. Thanx very much for this.

One other thing : when I disable javaScript and reload the page, only the current item is displayed in the carousels of my page. Of course, I'm no more talking about a carousel with dynamic content loading : I'm talking about a "normal" carousel, with for example 3 items in it, scrolled 1 by 1. If I disable jS, only 1 item (the current one) will be visible. So, from an accessibility point of view, there's a loss of informations : all the items of the carousels should be displayed (with a vertical or horizontal scrolling bar, for example). Is it a CSS problem ? Please note that I don't want you to do the job for me (in fact... you have ever done all the job for me ;)). I'd just like to know if, with the current 0.3 branch, we can have this sort of behaviour (I'm a bit better in CSS than in jS, so if this is just a CSS problem, I assume I can solve this by myself).

PS : the following code has no effect for the moment : $("#carousel-editos").bind('jcarouselinit,jcarouselscroll', function(event, carousel) { $("#editos .counter").text("").prepend(carousel.first().index() + " / " + carousel.list().size()); }).jcarousel({animation: 0}); It doesn't display the counter. Would anyone have an idea ?

jsor commented 12 years ago

Yep, its up to you how style it. Since jCarousel adds the class jcarousel you can do something like this for example:

.mycarousel {
    overflow: auto;
}

.mycarousel.jcarousel {
    overflow: hidden;
}

Regarding the counter. My mistake, multiple events must be separated by a space:

.bind('jcarouselinitend jcarouselscrollend', function(event, carousel) { 
    $("#editos .counter").text("").prepend((carousel.first().index() +  1)+ " / " + carousel.items().size()); 
})

I made a few corrections:

  1. Bind to the *end events
  2. Do index() + 1 because indexes are 0-based
  3. Use carousel.items().size()instead of carousel.list().size()
Dupond commented 12 years ago

Wow ! Thanx very much : it works fine !

So the only trouble left I can see is the annoying keyboard browsing issue. I've seen your "focus" example, but adding the following code to my carousel has no effect :

.focus(function(e) {
    e.preventDefault();
    e.stopPropagation();
    $(this).closest('.jcarousel').jcarousel('scroll', this);
});

Here is what it does when tabbing throughout the page using the keyboard :

  1. The focus goes to the first link inside of the first item of the carousel, even if it's not the first displayed item...
  2. When you keep browsing using the "tab" key, the focus goes to elements after the last displayed item...

The focus should go to the first link inside of the first displayed item, and, after the last link in the last displayed item, the focus should go out of the carousel.

Is it something which could be done via a plugin ? Or is it something that can already be done using the actual 0.3 code ? Something to settle up inside of the configuration javaScript file maybe ?

Thanx for your help... once more, Best regards, Dupond

jsor commented 12 years ago

If you want to use it like i did in the example, you have to set the tabindex attribute explicitly on each element.

Dupond commented 12 years ago

I can't do that, since I'd have to set the tabindex attribute to each link of my page, right ? But some parts of the page are shared with other pages of the blog, so this is just not possible for me.

Is there a way to use the "focus" callback in order to achieve this ? (I mean : the focus goes to the first link of the first displayed item, and then goes out after the last link of the last displayed item ?)

Also, I was wondering if jCarousel could be used to create a "more" link, so that more content is added (instead of "scrolled") at the end of a page : it's exactly the same as the carousel with dynamic content loading that you made for me, but without the "scrolling" function (the new content loaded is added at the end of the container, instead of replacing the old one). If possible, I'd like to stay with jCarousel, instead of adding another plugin again... but is this possible ?

Thanx, Best regards, Dupond

jsor commented 12 years ago

Regarding the focus stuff, i don't know actually. Maybe better ask on SO or something. Second question: Use the right tool for the job, jCarousel is not made for something like this.

Dupond commented 12 years ago

OK, I'll see what I can't do for the keyboard browsing issue. I had solved it with the old 0.2.8 version (it can be seen here), but it's been a reaaally hard work for me... and I've been unable to upgrade it so that it can be used with the 0.3 branch :(

Now I think it's time to close this looong post ;)

Thanx again for all the great help you gave to me !

Dupond commented 12 years ago

Just in case, I've asked the question about the keyboard browsing problem on SO.

Dupond commented 12 years ago

Unfortunately, I had no answer on SO. And no more luck on the jQuery forums...

... It's a great shame, since this is an important accessibility issue...

jsor commented 12 years ago

I'm still not sure what exactly the problem is. The topic has nothing to do with jCarousel, not event with JavaScript. If you solved it with your 0.2.8 version, you should be able to solve it for 0.3 as well.