JavaScript - 递归 - 迭代 JSON 并允许用户跳过

3
我将在我的网站上实现一个趋势功能,并想知道如何处理递归函数中的用户点击事件的最佳方法。
代码示例:
var trendingCodes = [];

// trendingCodes is json populated by ajax, this part works fine and not included for brevity
//   looks like {code:'SadPanda', text:'Thanks for checking out my !shouttag', img:false}

// Kick this pig
showTrendingCode(0, 200);

function showTrendingCode(indexCurrent, fadeSpeed)
{
    var indexNext       = ((indexCurrent + 1) % trendingCodes.length);
    var trendingCode    = trendingCodes[indexCurrent];

    if (trendingCodes.length > 1) {

        // !!!! Problem
        //  I am trying to register an event handler for the 'next' trending code
        //    but if I click multiple times it causes a 'maximum stack exceeded' error
        jQuery('#trending-refresh').off().on('click', function()
        {
            showTrendingCode(indexNext, 200);
        });

        jQuery('#trending-text p').add(jQuery('#trending-title span')).add(jQuery('#trending-refresh')).fadeIn(fadeSpeed);

        setTimeout(function () { showTrendingCode(indexNext, 600); }, 5000);
    }
}

从代码中可以看到,我正试图为一个DOM元素(next)注册事件处理程序,但如果用户多次点击该链接,将会得到“ maximum stack exceeded”错误。如果用户根本没有点击,则函数会一直运行(这是期望的效果)。

有什么建议可以使它按预期工作吗?

这是代码: https://jsfiddle.net/o1ekr6g3/


1
当你点击时,你会调用 showTrendingCode 两次,一次来自点击事件,一次来自你的 setTimeout,我相信这就是问题的根源。如果发生点击事件,你应该在 setTimeout 之前返回。 - RickTakes
你能否创建一个 StackSnippet 或 JSFiddle 来演示一下?尝试将 .on() 替换为 .one() - guest271314
更新了fiddle。@RickT:有道理。(at)guest:我会试一下的。 - Mike Purcell
2个回答

1
尝试创建一个变量来引用超时(如果已定义),使用 .stop()clearTimeout(),将 .on() 替换为 .one()
var timeout = null;

showTrendingCode(0, 400);

function showTrendingCode(indexCurrent, fadeSpeed) {
if (timeout) {
  clearTimeout(timeout);
  $("*").stop()
}
  var indexNext = ((indexCurrent + 1) % trendingCodes.length);
  var trendingCode = trendingCodes[indexCurrent];

  jQuery('#trending-text p').add(jQuery('#trending-title span')).fadeOut(fadeSpeed, function() {
    if (trendingCode.code) {

      jQuery('#trending-title span').html('<a href="/!/' + trendingCode.code + '">!' + trendingCode.code + '</a>');
      jQuery('#trending-text p').text(trendingCode.text);

      jQuery('#trending-moneyshot').attr('href', '/!/' + trendingCode.code);

      if (trendingCodes.length > 1) {

        jQuery('#trending-refresh').off("click").one('click', function() {
          clearTimeout(timeout);
          $("*").stop(true, true);
          showTrendingCode(indexNext, 200);

          return true;
        });

        jQuery('#trending-text p').add(jQuery('#trending-title span')).add(jQuery('#trending-refresh')).fadeIn(fadeSpeed);

        timeout = setTimeout(function() {
          showTrendingCode(indexNext, 600);
        }, 1000);
      } else {

        jQuery('#trending-text p').add(jQuery('#trending-title span')).fadeIn(fadeSpeed);
      }

    } else {

      jQuery('#trending-title span').text('!SADPANDA');
      jQuery('#trending-text p').text('Nothing is trending right now...');

      jQuery('#trending-text p').add(jQuery('#trending-title span')).fadeIn('slow');
    }
  });
}

jsfiddle https://jsfiddle.net/o1ekr6g3/1/

JSFiddle 链接

编辑,已更新

尝试在 showTrendingCode 之外定义 indexNext;用 .delay() 替换 setTimeout();使用 .promise().then().fail();在 showTrendingCode 之外声明 #trending-refreshclick 事件和处理程序。

var trendingCodes = [{
  code: 'FOOBAR',
  text: 'This is my !shouttag',
  img: false
}, {
  code: 'SADPANDA',
  text: 'This is another !shouttag',
  img: false
}];

var elems = jQuery("#trending-text p, #trending-title span, #trending-refresh");

jQuery("#trending-refresh").click(function() {
  elems.stop().promise().fail(function() {
    showTrendingCode(indexNext, 400)
  })
})

var indexNext;

showTrendingCode(0, 400);

function showTrendingCode(indexCurrent, fadeSpeed) {
  indexNext = ((indexCurrent + 1) % trendingCodes.length);
  var trendingCode = trendingCodes[indexCurrent];
  if (trendingCode.code) {

    jQuery('#trending-title span').html('<a href="/!/' + trendingCode.code + '">!' + trendingCode.code + '</a>');
    jQuery('#trending-text p').text(trendingCode.text);
    jQuery('#trending-moneyshot').attr('href', '/!/' + trendingCode.code);

    if (trendingCodes.length > 1) {
      elems.fadeIn(fadeSpeed).delay(3000)
        .promise()
        .then(function() {
          $(this).fadeOut(fadeSpeed).promise().then(function() {
            showTrendingCode(indexNext, fadeSpeed)
          })
        })

    } else {

      jQuery('#trending-title span').text('!SADPANDA');
      jQuery('#trending-text p').text('Nothing is trending right now...');

      jQuery('#trending-text p, #trending-title span').fadeIn('slow');
    }
  }
}
#trending-text p,
#trending-title span {
  display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div id="trending-wrap" class="clearfix">
  <a id="trending-moneyshot" href="javascript:void(0);">
    <img class="pull-left" src="/image/local/global/trending_image_default.png" />
  </a>
  <div id="trending-text-wrap">
    <div id="trending-title" class="spacerBottomMedium">
      Trending: <span class="bold"></span>
    </div>
    <div id="trending-text">
      <p></p>
      <a id="trending-refresh" href="javascript:void(0);" style="display:none;">
        <img class="pull-left" src="http://lorempixel.com/50/50/technics" />
      </a>
    </div>
  </div>
</div>

jsfiddle https://jsfiddle.net/o1ekr6g3/6/

可以翻译为:

jsfiddle https://jsfiddle.net/o1ekr6g3/6/


$("*").stop() 被包含在 .fadeIn() 中,用于停止当前正在进行的动画。 - guest271314
无法复现。尝试在1-5秒内点击下方图像三次;同一a元素不会连续显示。 - guest271314
我会仔细检查我的代码,可能有些地方我忽略了。 - Mike Purcell
嗯,我更新了我的代码,现在我可以重现这个重复/延迟问题:https://jsfiddle.net/o1ekr6g3/3/ - Mike Purcell
让我们在聊天中继续这个讨论 - Mike Purcell
显示剩余4条评论

0

我选择将迭代器函数与展示函数分离,而非使用递归,因此最终的结果如下:

jQuery.ajax({
    beforeSend: function() {
        $('#trending-text p').text('Loading...');
        $('#trending-title span').text('Loading...');
    },
    datatype: 'json',
    url: '<?php echo url_for('@ajaxTrending') ?>',
    success: function(data) {

        if (data.status == 'success') {

            jQuery.each(data.content, function(code, message)
            {
                if (message.length >= <?php echo $messageLength ?>) {
                    message = message.slice(0, <?php echo $messageLength ?>) + '...';
                }

                trendingCodes.push({code:code, img:false, text:message});
            });
        }

        else {

            trendingCodes.push({code:false, img:false, text:false});
        }

        // This makes the initial call to the iterator function
        setTimeout(function() { showTrendingCodes(); }, 500);
    }
});

// Iterator function
function showTrendingCodes()
{ 
    var counter = 1;

    // Show first code via separate function
    showTrendingCode(trendingCodes[0], 300);

    if (trendingCodes.length > 1) {
        setInterval(function()
        {
            showTrendingCode(trendingCodes[counter++], 300);

            if (counter >= trendingCodes.length) {

                counter = 0;
            }
        }, 3500);
    }
};

function showTrendingCode(trendingCode, fadeSpeed)
{
    var $moneyshot  = jQuery('#trending-moneyshot');
    var $refresh    = jQuery('#trending-refresh');
    var $title      = jQuery('#trending-title span');
    var $text       = jQuery('#trending-text p');

    $text.add($title).fadeOut(fadeSpeed, function()
    {
        if (trendingCode.code) {

            $moneyshot.attr('href', '/!/' + trendingCode.code);
            $text.text(trendingCode.text);
            $title.html('<a href="/!/' + trendingCode.code + '">!' + trendingCode.code + '</a>');

            $text.add($title).fadeIn(fadeSpeed);

        } else {

            $title.text('!SADPANDA');
            $text.text('Nothing is trending right now...');

            $text.add($title).fadeIn(fadeSpeed);
        }
    });
}

这个解决方案运行良好,不会导致OP中提到的错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接