Проблема с переменной областью в JavaScript - PullRequest
0 голосов
/ 16 июля 2009

Я быстро кодировал нечто вроде отображения продукта, которое получает половину своего ввода со страницы, а другую половину - из запроса AJAX.

Вот код ...

function productDisplay() {


    products = [];

    this.index = 0;

    setupProductDisplay();

    processListItems();

    showProduct();

    function setupProductDisplay() {

        var productInfoBoxHtml = '<div id="product-info"><h3 class="hide-me"></h3><span id="dimensions" class="hide-me"></span><div id="product-gallery"><img alt="" src="" /></div><ul id="product-options" class="hide-me"><li id="spex-sheet"><a href="" rel="external">Download full spex sheet</a></li><li id="enlarge-image"><a href="" rel="lightbox-gallery">Enlarge image</a></li></ul><div id="product-description" class="hide-me"></div><span id="top"></span><span id="bottom"></span><span id="side"></span><span class="loading"></span></div>';
        $('#products').after(productInfoBoxHtml);
    }

    function processListItems() {

        $('#products > li')
            .append('<span class="product-view">View</span>')
            .filter(':even')
            .addClass('even')
        .end()
            .each(function() {

                products.push({
                    id: $(this).find('h3').html(),      
                    title: $(this).find('h3').html(),
                    dimensions: $(this).find('.dimensions').html(),
                    description: $(this).find('.product-description').html()
                });

        })
        .find('.product-view')
            .click(function() {

                var $thisListItem = $(this).parents('ul li');

                var index = $('#products > li').index($thisListItem);

                this.index = index;

                showProduct();


            });

    };


    function showProduct() {

          var index = this.index;

          console.log('INDEX = ' + index);

        // hide current data
            $('#product-info')
            .show()
            .find('.hide-me, #product-gallery')
                .hide()
            .parent()
                .find('.loading')
                .show();



            // get data contained in the page

            $('#product-info')
            .find('h3')
                .html(products[index].title)
            .parent()
            .find('#dimensions')
                .html(products[index].dimensions)
            .parent()
            .find('#product-description')
                .html(products[index].description)


            // get id & then product extra info

            var id = $('#products > li').eq(index).attr('id').replace(/id-/, '');




            var downloadPath = PATH_BASE + 'downloads/';

            var imagePath = PATH_BASE + 'images/products/'

            $.getJSON(PATH_BASE + 'products/get/' + id + '/',
                function(data){           
                  var file = '';    
                  var images = [];

                  file = data.file;

                  images = data.images;

                  // show file list item if there is a file
                  if (file) {
                    $('#spex-sheet').show().find('a').attr( { href: downloadPath + file  } );   
                  } else {                  
                    $('#spex-sheet').hide();
                  }

                  // image gallery



                  if (images.length != 0) {
                    $('#product-gallery').show();
                    // preload image thumbnails
                    $.each(images, function(i, image){
                        var img = new Image();
                        img.src = imagePath + 'thumb-' + image;
                        img = null;
                    });

                    // set first image thumbail and enlarge link
                    if (images[0]) {
                        $('#enlarge-image').show().find('a').attr({ href: imagePath + images[0] });
                        $('#product-gallery img').attr ( { src: imagePath + 'thumb-' + images[0]} )

                    }

                    console.log(images);

                    // setup gallery

                    var currentImage = 0;

                    clearInterval(cycle);

                    console.log(cycle);



                    var cycle = setInterval(function() {
                        console.log(currentImage + ' = ' + index);
                        if (currentImage == images.length - 1) {            
                            currentImage = 0;               
                        } else {                
                            currentImage ++;                
                        };

                        var obj = $('#product-gallery');

                        var imageSource = imagePath + 'thumb-' + images[currentImage];          
                        obj.css('backgroundImage','url(' + imageSource  +')');      
                        obj.find('img').show().fadeOut(500, function() { $(this).attr({src: imageSource}) });
                        $('#enlarge-image a').attr({ href: imagePath + images[currentImage] });         
                    }, 5000);


                    // setup lightbox
                    $("#enlarge-image a").slimbox({/* Put custom options here */}, null, function(el) {
                        return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
                    });



                  } else {
                    // no images

                    $('#enlarge-image').hide();
                    $('#product-gallery').hide();

                  };


                  // show the product info
                  $('#product-info')
                    .find('.hide-me')
                        .remove('#product-gallery, #spex-sheet')
                            .show()
                 .parent()
                    .find('.loading')
                        .hide();

            });


    };




};

Важной функцией является showProduct (). Обычно я не пишу JS, как это, но я решил попробовать. Моя проблема в том, что когда пользователь нажимает кнопку «больше», и он отображает продукт, он не сбрасывает простое слайд-шоу (изображения сбрасываются, я думаю, что это может быть связано с setInterval (), может быть, или кажется, он каждый раз создает новый экземпляр showProduct ().

Кто-нибудь знает, что я делаю не так?

Ответы [ 2 ]

2 голосов
/ 16 июля 2009

Мне пришлось переформатировать ваш код, чтобы действительно понять, что происходит. Во всяком случае, я нашел проблему с кодом.

Как вы правильно догадались, проблема связана с областью действия, но не с переменной 'images', а с переменной 'cycle'. Почему?

Эта строка

var cycle = setInterval(function() {

Всегда создает новую переменную локального цикла (обратите внимание на 'var'), которая недоступна, когда showProduct вызывается во второй раз. Это означает, что эта строка

clearInterval(cycle);

по сути бесполезен, поскольку всегда передает null в функцию clearInterval и ничего не очищает. Это означает, что, продолжая нажимать на «больше», вы создаете все больше и больше вызовов функции setInterval, никогда не удаляя старые.

В любом случае, я немного реорганизовал ваш код, думаю, это должно работать так, как ожидалось. Я сделал следующие изменения:

  1. Удалена переменная this.index. Лучше передать 'index' в showProduct, чем устанавливать this.index перед вызовом метода showProduct и заставить showProduct использовать эту переменную. Кроме того, почему вы префикс переменной с этим?

  2. Объявлена ​​циклическая переменная вне области showProduct, локальная для метода productDisplay. Это гарантирует, что вы можете получить доступ к cycler во время различных вызовов showProduct.

  3. Созданы меньшие функции с именами showFile, showGallery, showProductInfo, чтобы упростить понимание / сопровождение кода.

Дайте мне знать, если у вас есть какие-либо вопросы ИЛИ если код все еще не работает.

function productDisplay() {

    //Instead of keeping this.index variable, it's better to make showProduct function
    //take index variable. 

    products = [];
    setupProductDisplay();
    processListItems();

    //We have to define cycler outside the showProduct function so that it's maintained
    //in between showProduct calls. 
    var cycler = null;

    showProduct(0);

    function setupProductDisplay() 
    {
        var productInfoBoxHtml = '<div id="product-info"><h3 class="hide-me"></h3><span id="dimensions" class="hide-me"></span><div id="product-gallery"><img alt="" src="" /></div><ul id="product-options" class="hide-me"><li id="spex-sheet"><a href="" rel="external">Download full spex sheet</a></li><li id="enlarge-image"><a href="" rel="lightbox-gallery">Enlarge image</a></li></ul><div id="product-description" class="hide-me"></div><span id="top"></span><span id="bottom"></span><span id="side"></span><span class="loading"></span></div>';
        $('#products').after(productInfoBoxHtml);
    }

    function processListItems() 
    {
        $('#products > li')
            .append('<span class="product-view">View</span>')
            .filter(':even')
            .addClass('even')
            .end()
            .each(
                function() 
                {
                    products.push({
                                    id: $(this).find('h3').html(),          
                                    title: $(this).find('h3').html(),
                                    dimensions: $(this).find('.dimensions').html(),
                                    description: $(this).find('.product-description').html()
                            });

                })
            .find('.product-view')
            .click( function()
                    {
                        var $thisListItem = $(this).parents('ul li');
                        showProduct($('#products > li').index($thisListItem));

                    }
                );

    };

    function showFile(file)
    {
        if (file)
        {
            $('#spex-sheet').show().find('a').attr( { href: downloadPath + file  } );       
        } 
        else 
        {                                      
            $('#spex-sheet').hide();
        }
    }

    function showGallery(images)
    {
        if(! images || !images.length || images.length == 0)
        {
            $('#enlarge-image').hide();
            $('#product-gallery').hide();
            return;
        }

        $('#product-gallery').show();

        $.each(images, 
                function(i, image)
                {
                    var img = new Image();
                    img.src = imagePath + 'thumb-' + image;
                    img = null;
                });

        // set first image thumbail and enlarge link
        if (images[0])
        {
            $('#enlarge-image').show().find('a').attr({ href: imagePath + images[0] });
            $('#product-gallery img').attr ( { src: imagePath + 'thumb-' + images[0]} )
        }

        var currentImage = 0;
        clearInterval(cycler);

        cycler = setInterval(
                function() 
                {
                    currentImage = currentImage == images.length - 1 ? 0 : currentImage++;
                    var obj = $('#product-gallery');

                    var imageSource = imagePath + 'thumb-' + images[currentImage];                  
                    obj.css('backgroundImage','url(' + imageSource  +')');          
                    obj.find('img').show().fadeOut(500, function() { $(this).attr({src: imageSource}) });
                    $('#enlarge-image a').attr({ href: imagePath + images[currentImage] });                 
                }, 5000);



        $("#enlarge-image a").slimbox({/* Put custom options here */}, null, function(el) {
                                        return (this == el) || ((this.rel.length > 8) && (this.rel == el.rel));
                                });

    };

    function showProductInfo()
    {
        $('#product-info')
            .find('.hide-me')
                .remove('#product-gallery, #spex-sheet')
                .show()
            .parent()
                .find('.loading')
                .hide();
    }

    function showProduct(index) 
    {
        $('#product-info')
            .show()
            .find('.hide-me, #product-gallery')
                .hide()
            .parent()
                .find('.loading')
                .show();

        // get data contained in the page
        $('#product-info')
            .find('h3')
                .html(products[index].title)
            .parent()
                .find('#dimensions')
                    .html(products[index].dimensions)
                .parent()
                .find('#product-description')
                    .html(products[index].description)

        // get id & then product extra info
        var id = $('#products > li').eq(index).attr('id').replace(/id-/, '');

        var downloadPath = PATH_BASE + 'downloads/';
        var imagePath = PATH_BASE + 'images/products/'

        $.getJSON(PATH_BASE + 'products/get/' + id + '/',
            function(data)
            {           
                showFile(data.file);
                showGallery(data.image);
                showProductInfo();

            });

    };




};
1 голос
/ 16 июля 2009

Если вы не определили свои переменные с помощью var (например, var images = ...;), тогда они будут считаться глобальными переменными (членами объекта window).

Если вы определите их с помощью var, то они будут видны функции целом (даже до объявления переменной), они будут объявлены в.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...