如何在委派的事件侦听器中使用油门/反跳功能? (香草JS)

时间:2019-08-25 18:12:46

标签: javascript

尝试优化某些JS的UX,包括使用事件委托和限制-但似乎无法结合使用这些方法。 我正在寻找一种监听事件(滚动,鼠标移动等),识别元素然后调用相关函数(限制)的方法。

我已经尝试了各种方法,包括尝试添加包装器/中间函数(处理程序)以传递多个函数-但是最终结果是相同的……我最终从Throttle函数中获得了任何回报。

我设法上班的唯一方法是产生一个多油门功能,在该功能中,我希望重复使用每个要油门的功能手动重复油门功能。

function mythrottle() {
    var timer1, timer2;
    return function() {
        var now = Date.now();
        var last1 = timer1;
        var last2 = timer2;

        if(!last1) { timer1 = now;  callback1();  return; }
        if(!last2) { timer2 = now;  callback2();  return; }

        if(last1 + 500 > now) { return; }
        timer1 = now;  callback1();

        if(last2 + 1500 > now) { return; }
        timer2 = now;  callback2();
    }
}

function callback1(){
    console.log("callback 1 firing");
}

function callback2(){
    console.log("callback 2 firing");
}

window.addEventListener('scroll', mythrottle());

理想情况下,我想要; 1)每个事件一个事件监听器 2)过滤/限定事件的能力(单击按钮或链接或跨度等) 3)然后使用特定函数(取决于过滤器/限定符)调用油门函数,并传递函数和延迟 4)节流阀延迟处理事件(立即着火,然后等待)

这将避免我当前要做的事情,即复制并为每个单独的东西准备好几个小块(并且我必须用限定符/过滤器包装每个!)。

1 个答案:

答案 0 :(得分:0)

有很多方法可以完成您想做的事情。无论如何,要使其动态工作(添加/删除/配置回调),您需要一个状态。这就是为什么我将所有内容都包装在一个类中的原因。

将分别跟踪每个回调(以使每个请求具有不同的延迟)。实例化之后,您可以添加更多的回调和/或事件。显然,可以扩展/改进此示例,但是我认为这是一个好的开始,它应该满足您的所有要求。

如果您对以下代码有任何疑问,请随时提出,我们将很乐意回答。

  • 注意:我使用了现代语法,但是可以很容易地重写它以提高兼容性。

// -- THROTTLER --

class Throttler {

    constructor(args) {

        this.queue = [];
        this.throttle = typeof args.throttle === "number" ? args.throttle : 500; // GLOBAL THROTTLE
        this.threshold = typeof args.threshold === "number" ? args.threshold : 50; // GLOBAL THRESHOLD

        // BIND METHODS
        this.addCallback = this.addCallback.bind(this);
        this.addEvent = this.addEvent.bind(this);
        this.handler = this.handler.bind(this);

        if (Array.isArray(args.queue)) {
            // SETUP INITIAL CALLBACKS
            args.queue.forEach(this.addCallback);
        }

        if (Array.isArray(args.events)) {
            // SETUP INITIAL EVENTS
            args.events.forEach(this.addEvent);
        }

    }

    addCallback(cb) {
        if (typeof cb === "function") {
            this.queue.push({
                throttle: this.throttle,
                threshold: this.threshold,
                callback: cb,
                timer: null,
                ref: Date.now(),
                elapsed: 0
            });
        } else if (typeof cb === "object") {
            this.queue.push({
                throttle: cb.debounce ? undefined : this.throttle,
                threshold: this.threshold,
                ...cb,
                timer: null,
                ref: Date.now(),
                elapsed: 0
            });
        }
    }

    addEvent(eventName) {
        // ATTACH HANDLER
        window.addEventListener(eventName, this.handler);
    }

    handler(e) {

        this.queue.forEach((elem) => {

            const NOW = Date.now();

            if (typeof elem.throttle === "number") { // THROTTLE

                elem.elapsed += NOW - elem.ref;
                elem.ref = NOW;

                if (elem.elapsed >= elem.throttle) {

                    // EXECUTE CALLBACK
                    if (typeof elem.callback === "function") {
                        if (typeof elem.selector !== "string" || (typeof e.target.matches === "function" && e.target.matches(elem.selector))) {
                            if (typeof elem.eventName !== "string" || e.type === elem.eventName) {
                                elem.callback(e);
                            }
                        }
                    }

                    // RESET COUNTER
                    elem.elapsed = 0;

                }

                // KILL TIMER
                elem.timer && clearInterval(elem.timer);

                // RE-CREATE TIMER
                elem.timer = setTimeout(() => {

                    // RESET COUNTER
                    elem.elapsed = 0;

                    // RESET TIMER
                    elem.timer = null;

                }, elem.threshold);

            } else if (typeof elem.debounce === "number") { // DEBOUNCE

                // KILL TIMER
                elem.timer && clearInterval(elem.timer);

                // RE-CREATE TIMER
                elem.timer = setTimeout(() => {

                    // EXECUTE CALLBACK
                    if (typeof elem.callback === "function") {
                        if (typeof elem.selector !== "string" || (typeof e.target.matches === "function" && e.target.matches(elem.selector))) {
                            if (typeof elem.eventName !== "string" || e.type === elem.eventName) {
                                elem.callback(e);
                            }
                        }
                    }

                    // RESET TIMER
                    elem.timer = null;

                }, elem.debounce);

            }

        });

    }

}



// --- USAGE ---

// INSTANTIATE THROTTLER
const myThrottler = new Throttler({
    throttle: 1500,
    events: ['scroll', 'mousemove'],
    queue: [callback1, callback2]
});

// ADD ANOTHER EVENT
myThrottler.addEvent('resize');

// ADD CONDITIONAL CALLBACK
myThrottler.addCallback({
    callback: callback3,
    selector: '*',
    eventName: 'mousemove'
});

// ADD CUSTOM DELAY DEBOUNCED CALLBACK
myThrottler.addCallback({
    callback: callback4,
    debounce: 2000
});

// ADD CUSTOM DELAY THROTTLED CALLBACK
myThrottler.addCallback({
    callback: callback5,
    throttle: 3000
});



// --- CALLBACKS ---

function callback1() {
    console.log("CB 1");
}

function callback2() {
    console.log("CB 2");
}

function callback3() {
    console.log("CB 3");
}

function callback4() {
    console.log("CB 4");
}

function callback5() {
    console.log("CB 5");
}