什么是(模拟按键的缺点?有更好的方法吗?

时间:2013-01-17 20:34:52

标签: javascript design-patterns javascript-events event-handling

假设我有以下代码,它监听输入元素,特别是在用户点击“Enter”时执行回调:

...

document.getElementById('human_input').onkeyup = (function (indicate) {
    return function(e) {
        var e = e || window.event; // for IE8
        if (e.keyCode === 13) { // if user presses 'Enter' key*
            indicate('User has submitted text');
        }
    }
})(indicate);

[* keyCode参考here。]

...

indicate所做的不是真正的重点,而是将一些代码应用于参数'User has submitted text'以在某处显示它。

现在,如果我希望某些脚本能够以相同的方式收听要填写的“隐形输入”,而不是通过人工而不是通过代码的单独部分进行监听,该怎么办?

假设名为fillHidden()的黑匣子脚本函数在某个指定点填写hidden_input字段。

document.getElementById('hidden_input').value = 'All filled out.'; // for example

非常独立的侦听器脚本模块如何找出值。说这样做是否足够......

document.getElementById('hidden_input').onkeyup = (function (indicate) {
    return function(e) {
        var e = e || window.event; // for IE8
        if (e.keyCode === 13) { // if user presses 'Enter' key
            indicate('User has submitted text');
        }
    }
})(indicate);

fillHidden()代码执行此操作:

document.getElementById('hidden_input').value = 'All filled out.'; // for example
document.getElementById('hidden_input').onkeyup({keyCode: 13}); // simulate keypress

或者那是不好的做法?关于how这样做甚至simulate keyboard shortcuts都存在问题,但我无法找到模拟按键的实际优点或其他方面。

是否有一种更好的方式,一种模式或一种互锁的回调系统,可以使这些模块在分离时进行交互?我已经看过Nicholas Zakas的视频将这种架构称为“松散耦合”,而我称之为 JavaScript Patterns 的书(Stefanov,2010)暗示了订阅者/发布者模型的可能性。与模拟按键相比,这些优点会有什么优势,特别是当模拟按键与用户的事件方形成一种对称时?在模拟按键时可能会有什么样的缺点/危险?

1 个答案:

答案 0 :(得分:2)

这一切都非常......抽象 这一切都很好。

SO的目标不一定是为这样的抽象提出解决方案,因为有一百万种方法可以做到这一点。

但是,有几个要点:

  1. 除非您以某种方式将模拟代码链接到用户生成的事件,否则浏览器可能会阻止您的模拟接管用户
    (想象一下浏览器让页面上的任何JS模拟任何按键或任何鼠标点击) 这通常意味着您使用某些不适合消费者使用的库,使用特定于浏览器的(即:FireFox / Chrome / IE)插件(用户必须安装/运行)进行紧密绑定,或者破产。选择一个(或更多)。

  2. 自定义事件,通过回调,通常可以让您将程序分开,但让它们一起运行。
    Zakas就Sandboxing做了一些很好的讨论,但这些都是企业级的,最终游戏类型的东西。他们非常棒,图书馆建设者和系统工程师应该完全考虑它们,但是为了制作平均页面,最好能够完成你需要编写的100行,而不是构建一个完整的框架。 ,如果一个库都包装了一个机箱中的每个模块,那么会将自身注入到该模块中。

  3. Pub-Sub(和观察员)/主持人(或调解员)发挥作用的地方。
    以上任何一种也可称为"发射器"或者"生成器"等等,具体取决于库。

  4. 上面的每一个都是以基本相同的方式创建的,并且做同样的事情 目标是将结果传递给想要倾听的观众 发言者选择何时通知观众,以及告诉他们什么 观众选择随时收听,等待下一次广播(可能永远不会来,但至少他们正在关注它),或者他们可以选择调出并停止收听广播。

    他们之间的区别在于"扬声器"了解发生的事情。

    <强>发布 - 订阅者


    publisher-subscriber中,说话者是使事情发生的对象。

    看看Twitter 您注册了一个Twitter帐户。一旦你拥有它,你可以跟随任何你想要的人 他们发推文的任何时候都会收到通知 任何人都可以关注你,所以每当你发推文时,他们都会收到通知。

    正在执行此操作的人会将该操作发布给任何想要听取该操作的订阅者。可能有3000个订阅者和一个发布者(简报),或3000个发布者和一个订阅者...... 可能会有不会订阅的发布商,或者不会发布的订阅者......但这是范例。

    <强>观察


    observer中,您正在谈论与正在完成工作的事物相关联的物体。
    它可能很紧张。它可能很松散。但是有一件事情做了事情,并且确实知道它在做什么。然后人们会对观察者进行更新。

    想想棒球的日子,人们通过无线电收听比赛 观察员将是广播评论员 他不是击球或偷基地的人。他是展台上的那个人,他看到了一切,并知道这一切意味着什么,并将其转变为用户友好的信息,供所有在家里听的人使用。

    现在,玩家可以直接向所有粉丝(pub-sub)发送有关播放的推文,我确信FourSquare会找到一种方法来获取他们的粉丝对于每个基准的准确性,可以实现三垒之王的不断更新(一次,在他狭窄的Z28中,不是杰夫)。

    <强>中介/主持人


    在这种情况下,我们谈论的是每个人都知道的对象,但没有人知道彼此。

    想象一下电话谈话电台节目。

    每个人都知道节目。每个人都可以打电话给节目,并与主持人交谈。但除了巧合之外,没有人知道其他听众。

    它与pub-sub略有不同,因为每个人都是发布商,但您不必知道某人的Twitter处理方式来听取他们的意见。你可以说Hey Twitter, any time anybody in the world mentions #browns, let me know. I'm starving. 它与observer略有不同,因为当主持人正在观看完成工作的人时,任何人都可以随时进行工作。


    哪一个是正确的?

    这一切都取决于你需要什么,以及你真正打算用它做什么。

    以下是我们如何成为主持人:

    var Moderator = function () {
        var events = {},
    
            notify = function (evtName, data) {
                var evt = events[evtName];
                if (!evt) { return; }
                evt.forEach(function (func) { func(data); });
            },
    
            listen = function (evtName, callback) {
                events[evtName] = events[evtName] || [];
                events[evtName].push(callback);
            },
    
            ignore = function (evtName, callback) {
                var evt = events[evtName];
                if (!evt) { return; }
                evt.forEach(function (func, i, arr) {
                    if (func === callback) { arr.splice(i, 1); }
                });
            };
    
        return { ignore : ignore,
                 listen : listen,
                 notify : notify  };
    };
    

    非常简单明了,对吧? 当然,这并不是特别充满了花里胡哨的东西,比如只在下次活动开始时订阅,或接下来的3次,或者其他什么......

    我们可能会这样使用它:

    var Game = function () {
    
        var game_moderator = Moderator(),
            scoreboard     = Scoreboard(),
            messages       = MessageBoard(),
            player_one     = Player(),
            player_two     = Player();
    
        function initialize () {
    
            player_one.initialize(game_moderator);
            player_two.initialize(game_moderator);
    
            game_moderator.listen("player:death", scoreboard.update);
            game_moderator.listen("player:death", messages.add_kill);
            game_moderator.listen("chat:input",   messages.add_msg );
    
        }
    
        function start() {}
        /* update... draw... etc... */
    
        return {
            load : load,
            initialize : initialize,
            start : start
        };
    };
    
    
    var game = Game(),
        loading = game.load();
    
    loading.done(function () {
        var initializing = game.initialize();
        initializing.done(game.start);
    });
    

    同时,Player可能如下所示:

    var Player = function (name) {
        var system,
    
            health = 30,
            damage = [],
    
            attack = function () { /* ... */ },
    
            hurt = function (amt, type, from) {
                health -= amt;
                damage.push({ amount : amt, type : type, from : from });
            },
    
            die = function () {
                var killing_blow = damage[damage.length - 1];
                killing_blow.player = name;
    
                system.notify("player:death", killing_blow);
            },
    
            update = function () {
                if (health <= 0) { die(); }
            },
    
            draw = function () {},
    
            initialize = function (sys) { system = sys; };
    
    
        return {
            initialize : initialize,
            update : update,
            draw   : draw,
            hurt   : hurt
            /* ... */
        };
    
    };
    

    回顾Game.initialize功能,我们可以看到我们已经有了一个记分牌和一个消息面板,这些记分牌和消息面板都会用&#34;玩家:死亡&#34;事件

    由于玩家被调用和定义的方式,我在初始化期间注入了对主持人的引用(这样我就可以将所有内容分开:依赖注入)。

    但是player_oneplayer_two一无所知,scoreboard对任何事情一无所知,只是偶尔会调用其.update方法并传入杀戮信息,{{1得到各种各样的爱,但它是每个人都陌生的......

    回到原来的问题:

    如果通过监视按键来填充messages,为什么不建立观察者?

    构建一个连接hidden-input事件监听器和keyup事件监听器的观察者。 让观察者将这些事件转化为有用的信息(例如:当你按住一个键时,keydown事件每秒会发射几十次 - 你可能不想要那个...所以警惕添加新密钥,或释放按下的密钥时发出警报。)

    keydown订阅。

    hidden-input已满,或者您的要求正在运行......并且您想要关闭某个事件时,请拥有一个全局主持人(或者是一个位于顶层的主持人) hidden-input所属的系统)。

    从那里开始一个名为hidden-input的事件或任何有意义的事件。

    关心这种事情的人可以通过主持人订阅该事件。

    当然,也就是说,如果你的程序是以没有人知道隐藏输入的方式构建的,那么有些人应该知道"hidden-input-filled"的事件。

    如果只有一组选定的内容应该了解hidden-input,那些是唯一应该了解其事件的内容,而hidden-input也应该能够了解它们,然后将它们hidden-input

    或混合搭配您的连接:
    我们的想法是建立有意义的沟通,并告诉人们他们需要知道什么,而不是更多。

    因此,如果Twitter用户应该是子酒吧,但页面上的不同小部件(时间线与搜索与近期图片等)不应该彼此了解多少(当然也不能使每张图片都能够与每个时间轴更新共享),然后创建一个全局主持人,整个小部件可以通过其他小部件进行通信(比如时间轴需要根据搜索结果进行更新),并且在每个小部件内部都有一个主持人和/或pub-sub用于组件之间的不同连接。

    希望有所帮助,我希望这可以解释为什么通过这样做更容易设计一个松散耦合的大型程序,而不是通过劫持真实事件,然后发射虚假的事件,打算针对您的计划的不同领域。

    老实说,如果你的完整网站及其所有程序都归结为:&#34;我已经得到了这个输入来做这件事,另一个输入做了另一件事&#34;,答案是它真的不重要。

    当你到达:&#34;我有一个包含8个空格的页面用于不同的小部件,并且有16个可能的小部件可以随时加载到这8个插槽中的任何一个,并且有某些小部件中的一些主要操作应该导致其他小部件中的响应,并且每个小部件需要在内部控制很多事件,我们需要在AJAX库和DOM / MVC(或MVVM)库中管道以控制所有每个小部件内部发生的事情本身,而且我只有一个......&#34;

    如果能够敲定这些东西并敲定Promises / Deferreds / Futures,并将你的大创意分解成更小的碎片,分散在生命周期的不同点上,这是个好主意。运行应用程序。