2

BOLD表示更新。

我有一个数组 ,steps其内容是具有与之关联的动作和元素的对象。像这样:

steps = [{action: 'click', element: <jQuery element>}, 
         {action: 'click', element: <jQuery element>}, ., ., N]

我想实现一个跑步者,他的工作是遍历数组中的每个元素并对元素执行特定的操作。每个步骤都必须按顺序执行。例如,如果您有:

 steps = [{action: 'click', element: <jQuery element representing a button>},
          {action: 'click', element: <jQuery element representing an anchor tag>}]

运行, run(steps, timeout), 将贯穿每一步。step[0].action 将在 step[0].element 上执行。由于 step[0] 可以创建要在 step[1] 中交互的 dom 元素(通过 AJAX),因此运行器需要等待特定的时间段(因此,超时),轮询 dom 以获取step[1].element 的存在。

这是我到目前为止的粗略看法:

var run = function() { 
    $.each(steps, function(i, v) { 
        var interval = 25, 
            start = 0, 
            timeout = 3000; 
        var i = setInterval(function(timeout) { 
            start = start + interval; 
            console.log(start); 
            if ($(v).is(':visible')) { 
                v.click(); 
                console.log('clicked', v); 
                clearInterval(i);
            }   
        }, interval);
    }); 
};

请注意,在上面的示例中,steps它只是一个 jquery 对象数组。它还不是所需的格式:

steps = [{action: 'click', element: <jQuery element>}, 
         {action: 'click', element: <jQuery element>}, ., ., N]

可以说,我需要遵循的“模式”是什么?我需要使用延迟对象来解决这个问题吗?它是用 setTimeout、setInterval 实现的吗?谢谢!

最终实施

var run = function(steps, interval, timeout) {
    var timer,
        time = 0,
        i = 0;

    runSingle(steps[0]);

    function abort() {
        console.log("Run aborted");
    }

    function runSingle(step) {
        timer = setInterval(function() {
            time += interval;
            if ($(step.element).is(':visible') === true) {
                clearInterval(timer);
                time = 0;
                $(step.element).trigger(step.action);
                (i < (steps.length - 1)) && runSingle(steps[++i]);
            } else if (time >= timeout) {
                clearInterval(timer);
                abort();
            }
        }, interval);
        console.log("Performed: ", step.action, "on", step.element) 
        if (i === (steps.length - 1)) console.log("Run successful");
    }
}
4

2 回答 2

1

首先,请注意,在您的示例中,变量将代表数组中的一个对象,因此说orv没有意义- 你想说or 。v.click()$(v).is(':visible')v.element.click()v.element.is(':visible')

如果你的意思action是一个字符串,它是一个 jQuery 方法的名称,并且element是一个 jQuery 对象,那么你可以这样做:

$.each(steps, function(i, obj) { 
    obj.element[obj.action]();
});

如果element是一个字符串,表示应该用于创建 jQuery 对象的选择器,那么:

$.each(steps, function(i, obj) { 
    $(obj.element)[obj.action]();
});

您不需要引入轮询概念,除非它action可能会异步执行某些操作,例如,如果它执行淡入或通过 Ajax 添加元素。

在您的示例中,您似乎申请是否继续当前步骤的唯一标准是当前元素是否可见。如果是这种情况,您可以执行以下操作:

var run = function(steps, delay, timeout) {
             var i = 0,
                 nextStep = function() {
                    if (i < steps.length) {
                       var step = steps[i],
                           retryDelay = 25,
                           retryTotal = 0,
                           intervalId = setInterval(function() {
                              retryTotal += retryDelay;
                              var $el = $(step.element);
                              if ($el.is(':visible')) { 
                                $el[step.action](); 
                                clearInterval(intervalId);
                                i++;
                                setTimeout(nextStep, delay);
                              } else if (retryTotal >= timeout) {
                                clearInterval(intervalId);
                              }
                           }, retryDelay);
                    };
                 }
             nextStep();
};

run(steps, 50, 3000);

run()函数定义了一个nextStep()用于setInterval不断检查当前元素是否可见的函数。一旦它是,它执行动作,清除间隔,并通过调用自身移动到下一个元素setTimeout

我不确定如何在轮询中使用超时概念,因为如果在指定的时间后当前元素不可见,你会怎么做?您不能真正继续下一个元素,因为它也可能取决于前面的步骤。我想你可以通过清除间隔而不再次调用来中止整个事情nextStep()编辑:我已经更新了代码以按照最后一句话工作。

于 2012-05-15T00:56:02.973 回答
1

这里有一些东西。我还没有彻底测试过:

var run = function(steps, interval)
{
    var timer,
        time = 0, timeout = 10000,
        ciel = steps.length - 1,
        i = 0;

    run_single(steps[0]);

    function run_single(item)
    {
        timer = setInterval(function()
        {
            var $el = $(item.selector);

            time += interval;

            if ( $el.length )
            {
                clearInterval( timer );
                time = 0;

                $el.trigger( item.action );

                i < ciel && run_single( step[ ++i ] );
            }
            else
            {
                if ( time >= timeout ) clearInterval( timer );
            }

        }, interval);
    }
};

var steps = [
    {action: 'click', selector: '#first'},
    {action: 'hover', selector: '#second'},
    {action: 'change', selector: '#third'}
    // and so on...
];

run(steps, 100);

在这里查看它的实际效果:http: //jsfiddle.net/myaeh/

于 2012-05-15T02:24:19.410 回答