我应该如何在javascript中制作复杂的连续事件

时间:2013-02-26 23:07:35

标签: javascript jquery

我正在开发一个用于JavaScript的交互式教程工具。该工具的核心是本教程的脚本。该脚本将触发运行动画的各种功能,扬声器声音加载新页面等。三个示例调用(大多数教程将有10到100个调用,因此非常需要一个简洁的调用概述:

wrap(); //wrap the page in an iframe
playsound('media/welcome') //playing a sound (duh)
highlight('[name=firstname]'); //animation that highlights an element.
playsound('media/welcome2');
loadpage(page2); //loading a new page

所有通话都有一些共同之处:它们具有非正常触发器。例如,在这个简单的脚本中,一旦加载第一个调用中的iframe,就应该触发第二个调用。一旦声音完成(即延迟),就会触发第三个脚本。动画完成后应触发第四个功能。第五个事件应该在事件上触发(例如点击)。

对此的技术解决方案是在前一个函数的回调中调用该函数,这有可能变得相当混乱。我喜欢一个解决方案,其中函数被称为精简版,这是一个有一点脑筋但没有编码经验的人可以锤击他们自己的脚本。你怎么解决这个问题?我对javascript很新,所以如果你能明确我会很感激。

2 个答案:

答案 0 :(得分:2)

我认为回调是你最好的选择。它们不一定非常混乱(尽管它们当然可能使它们完全不可理解)。您可以创建每个函数来接受回调,然后使用这样的结构以可读的方式按顺序调用它们:

var loadingSequence = {
    start : function() { wrap(this.playsound); },
    playsound : function() { playsound('media/welcome', this.highlight); },
    highlight : function() { highlight('[name=firstname]', this.playsound2); },
    playsound2 : function() { playsound('media/welcome2', this.loadpage); },
    loadpage : function()  { loadpage(page2); }
};

loadingSequence.start();

答案 1 :(得分:2)

我会使用每个构建的解决方案。有一个适合您的需求。像jTour那样简单的东西,或者如果不能覆盖像Scriptio那样复杂的东西。 this question的部分答案也可能对您感兴趣。

修改 如果你不想使用预先存在的解决方案,我会做这样的事情:

var runTutorial = (function () {

    // The command object holds all the different commands that can
    // be used by someone for the tutorial. Each of these commands
    // will recive a callback set as their `this`. This
    // callback should be called by your commands when they are done
    // running. The person making the tutorial won't need to know
    // about the callback, the code will handle that.
    var commands = {
        wrap: function () {
            //wrap the page in an iframe
            this();
        },
        playsound: function (soundPath, soundLength) {
            //playing a sound (duh)
            setTimeout(this, soundLength);
        },
        highlight: function (selector) {
            //animation that highlights an element.
            //I'm using jQuery UI for the animation here,
            // but most animation libraries should provide
            // a callback for when the animation is done similarly
            $(selector).effect('highlight', 'slow', this);
        },
        loadpage: function (pageUrl) {
            //loading a new page
            setTimeout(this, 500);
        },
        waitForClick: function () {
            // when we go into the click handler `this` will no
            // longer be availble to us since we will be in a
            // different context, save `this` into `that` so
            // we can call it later.
            var that = this;
            $(document).one('click', function () {
                that();
            });
        }
    },
    // This function takes an array of commands
    // and runs them in sequence. Each item in the
    // array should be an array with the command name
    // as the first item and any arguments it should be
    // called with following as the rest of the items.
    runTutorial = function (commandList) {
        var nextCommand = function () {
            if (commandList.length > 0) {
                var args = commandList.shift();

                // remove the command name
                // from the argument list
                cmd = args.shift(1);

                // call the command, setting nextCommand as `this`
                commands[cmd].apply(nextCommand, args);
            }
        }

        nextCommand();
    };

    return runTutorial;

}()); 

$('#tutorialbutton').click(function() {
    runTutorial([
        ['playsound', 'media/welcome', 1000],
        ['highlight', '[name=firstname]'],
        ['playsound', 'media/welcome2', 1500],
        ['waitForClick'],
        ['loadpage', page2],
        ['playsound', 'media/page2', 100]
    ]);
});

runTutorial函数采用一个简单的数组,其中包含命令的运行顺序及其参数。没有必要打扰使用回调编写脚本的人,runTutorial为他们处理。与需要编写者管理回调的系统相比,这有一些很大的优势。对于脚本中的每一行,您不需要使用显式回调的唯一名称,也不需要匿名函数的无限嵌套。您不需要重新连接任何内容来更改命令的播放顺序,只需在数组中重新排列它们即可。

jsfiddle you can play with

您的每个命令都需要在调用其回调(aka this)之前等待其操作完成。我使用setTimeout在小提琴中模拟这个。例如,如果您使用jQuery的.animate作为highlight,它会提供一个complete处理程序,在动画完成后触发,只需粘贴this(没有调用括号) ())那里。如果你使用的是jQuery UI,它有一个内置的'highlight' effect,所以你可以像这样实现它:

highlight: function (selector) {
    //animation that highlights an element.
    $(selector).effect('highlight', 'slow', this);
},

大多数提供动画的其他库应提供您可以使用的类似回调选项。

根据您的播放方式,控制声音的回调可能会更难。如果您使用的方法没有提供回调或轮询它以查看它是否已完成,您可能只需要向playsound添加另一个参数,该参数将以ms为单位获取声音的长度,然后在继续之前等待很长时间:

playsound: function (soundPath, soundLength) {
    //playing a sound (duh)
    setTimeout(this, soundLength);
},