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()
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>
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With