Как частично и постепенно перебирать объект javascript?

Я использую jQuery и Flickr API для возврата объекта json, содержащего много фотографий из фотопотока пользователя.

Я хотел бы перебрать полученный объект, чтобы сначала показать 40 фотографий, а затем кнопку «загрузить еще». При каждом нажатии этой кнопки к существующему списку фотографий будет добавляться следующая партия из 40 фотографий. Как только в объекте больше нет элементов, кнопка снова становится гиперссылкой на профиль пользователя Flickr.

На данный момент у меня есть рабочий код, отображающий первые 40 фотографий. Но когда нажимается кнопка «загрузить еще», я добавляю все оставшиеся элементы в HTML. Это потому, что я не смог найти способ создать поведение, описанное выше.

Возможно, есть более эффективный подход, который я тоже упустил.

Вот мой существующий код. Любые дополнительные комментарии или улучшения будут приветствоваться.

$(function () {
    myApp.uri = myApp.flickrUrl + '&per_page=' + myApp.maxNum + '&api_key=' + myApp.flickrApiKey + '&user_id=' + myApp.flickrUserId;

    myApp.getPhotos(myApp.uri, myApp.callback);
});

var myApp = {
    flickrApiKey : 'xxxx',
    flickrUserId : 'xxxx',
    flickrUrl : 'http://api.flickr.com/services/rest/?format=json&extras=url_t&method=flickr.people.getPublicPhotos',
    callback : 'jsonFlickrApi',
    minNum : 40,
    maxNum : 500
};

myApp.getPhotos = function (u, c) { 
    var jxhr = $.ajax({
        dataType : 'jsonp',
        url : u,
        jsonpCallback : c,
        timeout : 5000
    })

    .success(function (data, status) { 
        var photosContainer = $('#photos');
        photosContainer.prepend('<ul/>');
        var photosList = $('#photos ul');

        // while there are more photos to load, the default link text
        // is replaced with a string stored in a data-* attribute
        var moreLink = $('#more');
        var moreLinkText = moreLink.text();
        var moreLinkTextJs = moreLink.attr('data-text-js');
        moreLink
         .attr('data-text-nojs', moreLinkText)
         .text(moreLinkTextJs)
         .insertAfter(photosContainer);

        // initially populate the Photo List with the first 40 photos
        $.each(data.photos.photo, function (i, item){
            if (i < myApp.minNum) {
                var photoEl = '<li><a href="' + 'http://www.flickr.com/photos/' + item.owner + '/' + item.id + '" target="_blank"><img src="' + item.url_s + '" alt="' + item.title + '"></a></li>';
                $(photoEl).appendTo(photosList);
            }
        });

        // click on "More" link loads in all remaining photos.
        // would like this to load the next 40 in the object
        // each click until there are no items left...at which point unbind
        moreLink.live('click', function (e) {
            e.preventDefault();
            $.each(data.photos.photo, function (i, item){
                if (i >= myApp.minNum) {
                    var photoEl = '<li><a href="' + 'http://www.flickr.com/photos/' + item.owner + '/' + item.id + '" target="_blank"><img src="' + item.url_s + '" alt="' + item.title + '"></a></li>';
                    $(photoEl).appendTo(photosList);
                } 
            });

            // unbind events and revert the link text
            moreLink.text(moreLinkText).blur().die();
        });
    })

    .error(function (status) { 
    });
};

person user638118    schedule 28.02.2011    source источник
comment
Что-то, что вы можете сделать, чтобы повысить производительность, это выйти из цикла, если вам не нужно перебирать оставшиеся записи api.jquery.com/each jsfiddle.net/vj9JW   -  person stanwilsonjr    schedule 01.03.2011


Ответы (2)


Я бы посоветовал посмотреть шаблоны

http://api.jquery.com/category/plugins/templates/

На самом деле вы можете просто передать JSON в шаблон и добавить его. В вашем коде нет циклов :-)

Вот учебник:

http://www.borismoore.com/2010/09/introduction-jquery-templates-1-first.html

РЕДАКТИРОВАТЬ:

Чтобы преобразовать возвращенный JSON в 40 фотоблоков, используйте метод array.slice в массиве data.photos.

Что-то типа

var number_of_blocks = Math.floor(data.photos / 40)
var blocks = [];
var last_block_starts_at = number_of_blocks * 40;

for (var i = 0 ; i < number_of_blocks ; i++;)
{
blocks << data.photos.slice(i,40+i);
}
blocks << data.photos.slice(last_block_starts_at);

Теперь вы можете перебирать блоки, пока массив не закончится...

person macarthy    schedule 28.02.2011
comment
+1: Написание собственного кода для генерации HTML дублирует усилия по всему коду. Шаблоны помогают создавать поддерживаемый и менее подверженный ошибкам код. Однако я предпочитаю использовать такой инструмент, как code.google.com/closure/ шаблоны/docs/helloworld_js.html - person Juan Mendes; 28.02.2011
comment
@Mendes: я использовал шаблоны раньше, но не понимаю, как это отвечает на мой вопрос о просмотре объекта партиями по 40 элементов. Также кажется, что много лишнего кода. Почему включение плагина шаблона более эффективно, чем использование $.each в этом случае? - person user638118; 28.02.2011

Я получил этот код с одной из моих страниц. Я думаю, это что-то вроде того, что ты хотел. извините, строки не задокументированы ... но я уверен, что вы можете извлечь из них логику.

function photoLookUp(btn, ImageToUpdateId, LookUpWindowId, ImageContainerId, ImageSize) {

    var wnd = $(LookUpWindowId);
    if (wnd.active) return (wnd.style.display == "none") ? show() : hide();

    var wndResize = wnd.children[1].children[0];
    var ImageToUpdate = $(ImageToUpdateId);
    var imgContainer = $(ImageContainerId); imgContainer.innerHTML = "";

    wndResize.onmousedown = resizeOnMouseDown;
    wnd.onmousemove = function (e) { wnd.tHide = false }
    wnd.onmouseout = function (e) { wnd.tHide = true }
    wnd.active = true;

    var ldr = document.createElement("img");
    ldr.src = "Images/preloader.gif";
    ldr.title = "Loading, please wait...";
    ldr.onmouseover = function (e) { wnd.tHide = false }
    imgContainer.appendChild(ldr);

    show();

    var svc = new WADService();
    var par = { UserID: User.Profile.UserID };
    var dat = svc.get("GetUserPhotos", false, par);

    if (dat.length > 0)
        load(dat.shift(), 15);
    else
        imgContainer.innerHTML = "no photo collection";

    function load(Photo, cnt) {
        var par = { ImageID: Photo.ImageID, ImageSize: ImageSize };
        svc.get("GetImageURL", true, par).onreadystatechange = function (e) {
            if (this.readyState == 4) {
                var res = svc.deserialize(this.responseText);
                if (res) render(res);
            }
        }
        function render(res) {
            Photo.ImageURL = res;
            var obj = new PhotoObj(Photo).frameElement;
            imgContainer.insertBefore(obj, ldr)
            if (dat.length > 0 && --cnt > 0) {
                ldr.onclick = null;
                ldr.src = "Images/preloader.gif";
                ldr.title = "Loading, please wait...";
                load(dat.shift(), cnt)
            } else if (dat.length > 0) {
                ldr.onclick = loadMore;
                ldr.src = "Images/ViewPhoto.png";
                ldr.title = "View more...";
                //none of the ff lines work, ugh! how can i scroll my div in firefox ?!?!
                //imgContainer.scroll(0, imgContainer.scrollHeight - imgContainer.clientHeight);
                //imgContainer.scroll(0, imgContainer.scrollHeight);
            } else {
                imgContainer.removeChild(ldr);
            }
        }
        function loadMore(e) {
            ldr.onclick = null;
            load(dat.shift(), 16);
        }
    }
    function show() {
        wnd.style.display = "inline-block";
        btn.children[0].src = "Images/ArrowU.gif";
        wnd.tHide = true;
        wnd.tInterval = setInterval(_hide, 2000);
        function _hide(e) {
            if (wnd.tHide) { clearInterval(wnd.tInterval); hide() }
        }
    }
    function hide(e) {
        wnd.style.display = "none";
        btn.children[0].src = "Images/ArrowD.gif";
    }
    function resizeOnMouseDown(e) {
        var X = e.clientX, Y = e.clientY;
        var W = imgContainer.offsetWidth, H = imgContainer.offsetHeight;
        wndResize.style.cursor = "se-resize";
        document.onmousemove = function (e) {
            imgContainer.style.width = Math.max(72, (W + e.clientX - X)) + "px";
            imgContainer.style.height = Math.max(72 + 16, (H + e.clientY - Y)) + "px";
            return false;
        }
        document.onmouseup = function (e) {
            document.onmousemove = null; document.onmouseup = null;
            wndResize.style.cursor = "default";
        }
        return false;
    }
    function PhotoObj(Photo) {
        var img = document.createElement("div");
        this.frameElement = img;
        img.title = Photo.Title;
        img.ImageID = Photo.ImageID;
        img.ImageURL = Photo.ImageURL;
        img.style.backgroundImage = "url(" + Photo.ImageURL + ")";
        img.onmouseover = function (e) { wnd.tHide = false }
        img.onclick = function select(e) {
            ImageToUpdate.ImageID = img.ImageID;
            ImageToUpdate.ImageURL = img.ImageURL;
            ImageToUpdate.style.backgroundImage = img.style.backgroundImage;
            hide();
        }
    }
}
person winston    schedule 01.03.2011