Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chain delayed function using vanilla Javascript ES6

Tags:

javascript

How do you implement delay like the jQuery Library? - I know this question has been asked so many times, but haven't seen anyone implement it using async/await or ES6 stylistics. let me know if you have ideas

//create a jquery like library
class DOM {
    constructor(selector){
        this.elements = [];
        if(!selector){
            return;
        } else if(selector === 'document' || selector === 'window'){
            this.elements = [selector];
        } else {
            this.elements = Array.from(document.querySelectorAll(selector));
        }
    }

    on(){
        console.log('on');
        return this;
    }

    get(){
        console.log('get');
        return this;
    }


    delay(ms, callback){
        //how to implement this? how to chain result of callback onto next chain?
        console.log('delay');
        const promise = Promise.resolve();
        return promise.then(function(resolve) {
            setTimeout(function(){
                resolve(this);
            }, ms);
        });
    }
}

const $ = function(selector) {
    return new DOM(selector);
}       

$('document').on().delay(10000).get()
like image 870
guest Avatar asked Jun 15 '26 06:06

guest


2 Answers

You probably don't need promises or async/await at all, I think you can create a Proxy object that intercepts subsequent call.

The idea is that, when .delay(duration) is called, it'll return a proxy object instead of the class instance. This proxy object will intercept a method call, set time out for duration, then call the method with the original class instance.

class J {
  constructor(selector) {
    this.$element = document.querySelector(selector)
  }
  delay(duration) {
    const proxy = new Proxy(this, {
      get: (target, prop) => {
        const f = target[prop]
        return (...args) => {
          setTimeout(() => {
            return f.apply(target, [...args])
          }, duration)

          // return the class instance again, so subsequent call & delay still works
          return this
        }
      }
    })
    return proxy
  }
  text(content) {
    this.$element.textContent = content
    return this
  }
}

const $ = selector => new J(selector)

$('#test').text('hello').delay(1000).text('world')
<div id="test"></div>
like image 178
Derek Nguyen Avatar answered Jun 17 '26 19:06

Derek Nguyen


You could maintain a queue of functions still to execute on the selected element(s). That way you can allow multiple delays in the chain and also allow the client to stop the action.

A proxy can be used to "decorate" the methods which make sense to be delayed, so that they can be put in the queue instead of executed whenever a timer is still active.

Here is how that could look:

class DOM {
    constructor(selector) {
        this.elements = typeof selector === "object" ? [selector]
            : selector === 'document' || selector === 'window' ? [document] 
            : Array.from(document.querySelectorAll(selector));
        this.delayed = false;
        this.queue = [];
        const proxy = new Proxy(this, {
            get(obj, prop) {
                return !["css","show","hide","delay"].includes(prop) || !obj.delayed ? obj[prop]
                    : function (...args) {
                        obj.queue.push(() => proxy[prop](...args));
                        return this;
                    }
            }
        });
        return proxy;
    }
    each(cb) {
        this.elements.forEach(cb);
        return this;
    }
    css(name, value) {
        return this.each(elem => elem.style[name] = value);
    }
    show() {
        return this.css("display", "");
    }
    hide() {
        return this.css("display", "none");
    }
    on(eventType, cb) {
        return this.each(elem => elem.addEventListener(eventType, cb.bind(elem)));
    }
    delay(ms) {
        this.delayed = true;
        setTimeout(() => {
            this.delayed = false;
            while (this.queue.length && !this.delayed) this.queue.shift()();
        }, ms);
        return this;
    }
    stop() {
        this.queue.length = 0;
        return this;
    }
}

const $ = selector => new DOM(selector);    

const $span = $('#demo').hide();
for (let i = 0; i < 100; i++) {
    $span.delay(500).show()
         .delay(500).css("color", "red")
         .delay(500).css("color", "blue")
         .delay(500).hide();
}
$("#stop").on("click", function () {
    $span.stop();
    $(this).hide();
});
<div>This is a <span id="demo">colorful </span>demo</div>
<button id="stop">Stop</button>
like image 41
trincot Avatar answered Jun 17 '26 19:06

trincot



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!