How to debounce an event? (wrong Syntax?) - javascript

I'am a little bit confused. I would like do debounce the function resizeAd on windows-resize.
I played with this code but without any result. The debouncing is not done.
How I have to call the debouncing-function in this case?
The debouncing function works fine by calling them like this: var resizeIframeAd = debounce(function() {... }
(function(window, document, undefined) {
'use strict';
/*
* Global api.
*/
var adTech = window.adTech = {
get: function() {
return _instance;
},
//Main entry point.
init: function(options) {
return _instance || new ADTECH(options);
}
};
/**
* Constructor.
*/
var ADTECH = function(options) {
var defaultOptions = {
adID : '5202402',
hiddenClassName : 'hidden'
};
this.options = this.extend(options, defaultOptions);
this.makeAd();
_instance = this;
return _instance;
}
ADTECH.prototype = {
extend: function(source, target) {
if (source == null) { return target; }
for (var k in source) {
if(source[k] != null && target[k] !== source[k]) {
target[k] = source[k];
}
}
return target;
},
log: function(msg){
if(window.console){
console.log(msg);
}
},
// Debounce function
debounce: function(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
},
// event Handler
addEvent: function (elem, type, eventHandle) { console.log("adEvent is undefined: ",eventHandle);
if (elem == null || typeof(elem) == 'undefined') return;
if (elem.addEventListener) {
elem.addEventListener(type, eventHandle, false);
} else if (elem.attachEvent) {
elem.attachEvent("on" + type, eventHandle);
} else {
elem["on" + type] = eventHandle;
}
},
//to debounce this fucntion I call them like this var resizeIframeAd = debounce(function() { HOW TO DO THAT HERE?
resizeAd: function(invokeLater) {
// this is explicitly set based on the calling object.
var obj = this;
var helper = function () {
obj.log("resizeAd2 done.");
//var ad = document.getElementById(obj.options.adID); //obj.log(ad);
obj.debounce(function() {
console.log("please debounce this function");
}, 250)
};
// if invokeLater is a falsey value do the resizing right away
// if it is truthy return helper so that it can be assigned as
// an event handler
return invokeLater ? helper : helper();
},
// insert ad
makeAd: function () {
this.addEvent(window, "resize", this.resizeAd(true));
}
}
// Singleton
var _instance;
}(window, document));
var API = adTech.init();

I think you want to debounce your helper function:
var helper = this.debounce(function () {
obj.log("resizeAd2 done.");
//var ad = document.getElementById(obj.options.adID); //obj.log(ad);
console.log("please debounce this function");
}, 250);

Related

How to disable method call if instance of object doesn't exist (javascript)

Is there a way to not call a method from the function object (instance of it) if that instance doesn't exist?
This is my function for sticky sidebar and it has 2 methods;
init() and updateSticky();
function stickySideBar(element, options = {}) {
var _this = this;
_this.init = function () {}
_this.updateSticky = function (timeout) {
//this is actually just a debouncer that calls init method
}
}
I want to use this updateSticky on window resize
$(window).on("resize", function () {
newsletterBlog.updateSticky();
sideBarBlog.updateSticky();
if ($(productsSticky.element).length > 0) {
productsSticky.updateSticky();
}
});
now I use if loop to check if an element exists but I would like to do that inside of the instance of that function
if i dont have if loop i get "Uncaught TypeError: e.updateSticky is not a function".
cheers
EDIT
here is the function
function stickySideBar(element, options = {}) {
var _this = this;
console.log("_this :>> ", _this);
//declared element
_this.debouncer;
_this.element = element;
if ($(_this.element).length === 0) {
return;
}
// declared options
_this.parentElementClass = options.parentElementClass;
_this.wrapClass = options.wrapClass;
_this.activeStickyClass = options.activeStickyClass;
_this.top = options.top;
_this.width = options.width;
_this.activeBottomClass = options.activeBottomClass;
_this.disableOnMobile = options.disableOnMobile ? options.disableOnMobile : true;
_this.breakpoint = 992;
_this.init = function () {
};
_this.updateSticky = function (timeout) {
if ($(_this.element).length === 0) return;
var timeoutVal = timeout ? timeout : 100;
clearTimeout(_this.debouncer);
_this.debouncer = setTimeout(function () {
_this.init();
}, timeoutVal);
};
return _this.init();
}

Creating a method similar to delay in my own class

I am trying to create a method similar to delay() from JQuery.
I am creating a method called $. Remember I am not using JQuery and don't want to for this problem.
function $(element) {
if(!(this instanceof $)) {
return new $(element);
}
this.element = document.querySelector(element);
}
$.prototype.color = function color(color) {
this.element.style.color = color;
}
I can use this method like so:
$('#foo').color('red);
It will change the color of #foo to red
What I am trying to do is set a delay before it changes the color. One way would be to do :
$.prototype.delay(time, fn) {
setTimeout(function() {
fn();
}, time);
}
and then call it like so:
$('#foo').delay(1000, function() {
$('#foo').color('red');
});
But that's not very useful, what I would like to do instead is use it like so:
$('#foo').delay(1000).color('red);
I found this but couldn't figure it out.
Thanks in advance,
I.L
One way of doing it (#georg)
// create a new instance if it doesn't already exists when $ is called
function $(element) {
if(!(this instanceof $)) {
return new $(element);
}
this.element = document.querySelector(element);
this.promise = Promise.resolve(this);
this.css = this.element.style;
}
// wrapper for the promise
$.prototype.method = function(name, fn) {
$.prototype[name] = function(...args) {
this.promise.then(self => fn.apply(self, args));
return this;
};
}
// delay method,
$.prototype.delay = function(time) {
this.promise = new Promise(
resolve => setTimeout(() => resolve(this), time));
return this;
}
// example of a method to change the color
$.prototype.method('color', function (color) {
this.css.color = color;
});
// used like so
$('#foo').delay(2000).color('green');
<div id="foo">Hi there!</div>
I have found another nice solution that allows to use delay multiple time see my answer for more details.
Here's an example with promises. Requires some more work to be practical, but should give you an idea. Basically, all methods like color should operate on "resolved this" instead of just "this":
function $(element) {
if(!(this instanceof $)) {
return new $(element);
}
this.element = document.querySelector(element);
this.promise = Promise.resolve(this)
}
$.prototype.color = function color(color) {
this.promise.then(function(self) {
self.element.style.color = color;
});
}
$.prototype.delay = function(n) {
this.promise = new Promise(
resolve => setTimeout(() => resolve(this), n));
return this;
}
$('#foo').color('red');
$('#foo').delay(1000).color('blue');
<div id="foo">foo</div>
For automated promisifying you can use a wrapper like this:
$.prototype.method = function(name, fn) {
$.prototype[name] = function(...args) {
this.promise.then(self => fn.apply(self, args));
return this;
};
}
and then, for example,
$.prototype.method('color', function (color) {
this.element.style.color = color;
});
Here is how jQuery implement it :
function (time, type) {
time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
type = type || "fx";
return this.queue(type, function () {
var elem = this;
setTimeout(function () {
jQuery.dequeue(elem, type);
},
time);
});
}
Using only* Queue :
function (type, data) {
if (typeof type !== "string") {
data = type;
type = "fx";
}
if (data === undefined) {
return jQuery.queue(this[0], type);
}
return this.each(function () {
var queue = jQuery.queue(this, type, data);
if (type === "fx" && queue[0] !== "inprogress") {
jQuery.dequeue(this, type);
}
});
}
And Dequeue :
function (type) {
return this.each(function () {
jQuery.dequeue(this, type);
});
}
The only external jQuery call in these functions is "jQuery.fx", with can be avoided.
From there, it will be easy to implement these 3 functions in your own model.
Using chainable methods and setting a delay method:
function $(element) {
if(!(this instanceof $)) {
return new $(element);
}
// select all elements with this identifier
this.elements = document.querySelectorAll(element);
// by default select the first element of querySelectorAll
this.element = this.elements[0];
this.css = this.element.style;
// first method applied will be exectuted directly
this.delayTime = 0;
}
$.prototype.$ = function position(pos) {
if(pos == 'first') {
pos = 0;
} else if(pos == 'last') {
pos = this.elements.length-1;
}
var that = this;
setTimeout(function() {
that.element = that.elements[pos] || that.elements[0];
that.css = that.element.style;
}, this.delayTime);
return this;
}
$.prototype.delay = function(delayTime) {
// set a delay for the following method applied
this.delayTime += delayTime;
return this;
}
// wraps the method into a setTimeout
$.prototype.method = function(name, fn) {
$.prototype[name] = function(...args) {
var that = this;
setTimeout(function() {
// method will only take one relevant parameter
fn(that, args[0]);
}, this.delayTime);
return this;
};
}
// CSS methods
$.prototype.method('backgroundColor', function(self, color) {
self.css.backgroundColor = color;
})
$('#foo').delay(1000).backgroundColor('blue').delay(1000).backgroundColor('white').delay(1000).backgroundColor('red');
#foo {
background-color: lightgrey;
}
<div id="foo">Hey there!</div>

How to limit or throttle the number of function calls (in order to increase performance) in this function call?

I have a function that look like this:
function someFunction(text) {
$('.class').each(function() {
var $this = $(this);
if ($this.text().match(text)) {
$this.addClass('found');
} else {
$this.removeClass('found');
}
});
}
and that function is executed in keyup event,
$('input[type=text]').keyup(function() {
someFunction($(this).val());
});
on IE if there are lot of .class elements it can be slow, I thought that I could speed things up if I stop executing the each call if the function have been executed again before each is finished. How can I do this?
You could speed this up by adding a slight delay to the keyup handler so that the DOM is only affected after typing has ended instead of on each keystroke. You can also use the :contains selector to find the elements. Try this:
function someFunction(text) {
$('.class').removeClass('found').filter(':contains("' + text + '")').addClass('found');
}
var timer;
$('input[type=text]').keyup(function() {
clearTimeout(timer);
timer = setTimeout(function() {
someFunction(this.value);
}, 100);
});
Also, as mentioned by #A.Wolff you could cache the .class selector, but this would only work if no .class elements are dynamically added to the DOM while you're searching:
var $elements = $('.class');
function someFunction(text) {
$elements.removeClass('found').filter(':contains("' + text + '")').addClass('found');
}
var timer;
$('input[type=text]').keyup(function() {
clearTimeout(timer);
timer = setTimeout(function() {
someFunction(this.value);
}, 100);
});
Try adding return false; end of your function
function someFunction(text) {
$('.class').each(function() {
var $this = $(this);
if ($this.text().match(text)) {
$this.addClass('found');
} else {
$this.removeClass('found');
}
});
return false;
}
As I misunderstood the question, here're two optimizations instead (both can be used together):
Cache the query result, only occasionally updating it by timer
Not really beautiful but may provide a robust solution given that the performance gain is critical.
window.lastValue = null;
window.cachedElements = null;
window.updateCachedElements = function(){ cachedElements = $('.class'); };
function someFunction() {
cachedElements.each(function() {
var $this = $(this);
if ($this.text().match(lastValue)) {
$this.addClass('found');
} else {
$this.removeClass('found');
}
});
}
$('input[type=text]').keyup(function() {
if(cachedElements === null) {
updateCachedElements();
}
lastValue = $(this).val()
someFunction();
});
setInterval(function(){ updateCachedElements(); someFunction(); }, 500);
Use debounce (a form of throttling) to minimize number of someFunction calls to 1/100ms or 10 per second
After (and outside) the someFunction definition, do:
someFunction = debounce(someFunction, 100);
Debounce implementation from underscore.js:
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
var later = function() {
var last = (Date.now || new Date().getTime()) - timestamp;
if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) {
result = func.apply(context, args);
if (!timeout) context = args = null;
}
}
};
return function() {
context = this;
args = arguments;
timestamp = _.now();
var callNow = immediate && !timeout;
if (!timeout) timeout = setTimeout(later, wait);
if (callNow) {
result = func.apply(context, args);
context = args = null;
}
return result;
};
};

Chained promises and prototype `this`

I'm having a hard time to get promises to work with the right this scope inside prototypes.
Here is my code:
'use strict';
angular.module('testApp').factory('UrlSearchApi',
function($resource, URL_SEARCH_API, PAGE_SIZE, $q){
var resource = $resource(URL_SEARCH_API);
resource.Scroll = function () {
return this.reset();
};
resource.Scroll.prototype.reset = function () {
this.visibleItems = [];
this.allItems = [];
this.busy = null;
return this;
};
resource.Scroll.prototype.fetch = function(query){
var params = {};
if(query) { params.q = query; }
return resource.query(params).$promise;
};
resource.Scroll.prototype.loadAllItems = function (results) {
var d = $q.defer();
angular.forEach(results, function (result, i) {
this.allItems.push(result);
if(i === results.length - 1 ) { d.resolve(); }
}, this);
return d.promise;
};
resource.Scroll.prototype.loadVisibleItems = function () {
var length = this.visibleItems.length,
offset = parseInt(length / PAGE_SIZE),
start = PAGE_SIZE * offset,
end = start + PAGE_SIZE,
subset = this.allItems.slice(start, end),
d = $q.defer();
angular.forEach(subset, function (item, i) {
this.visibleItems.push(item);
if(i === subset.length - 1 ) { d.resolve(); }
}, this);
return d.promise;
};
resource.Scroll.prototype.nextPage = function (query) {
if(this.busy) { return; }
console.log('nextPage ', query);
var tasks = [],
that = this;
if(!this.allItems.length) {
this.reset();
this.busy = true;
return this.fetch(query)
.then(this.loadAllItems)
.then(this.loadVisibleItems)
.finally(function () {
this.busy = false;
});
} else {
this.busy = true;
return this.loadVisibleItems().finally(function () {
this.busy = false;
});
}
};
return resource;
});
Whenever I run the tests I get
describe('#nextPage', function () {
var scroll;
describe('when there is NO search term (show all)', function () {
beforeEach(function (done) {
scroll = new UrlSearchApi.Scroll();
$httpBackend.expectGET('/policy/search')
.respond(200, arrayGenerator(123));
scroll.nextPage().then(done);
$httpBackend.flush();
$rootScope.$apply();
});
it('should load all the items in all items variable', function () {
expect(scroll.allItems.length).toBe(123);
});
});
});
I get the following error:
TypeError: 'undefined' is not an object (evaluating 'this.allItems')
I now that $q in strict mode sets the this inside then to undefined. I tried using bind(this) in multiple places but not luck... Any ideas?
I've already answered a question like this here.
Just let me know in comments if you still have questions.
Upd. Try to update your resource.Scroll.prototype.nextPage method like this:
if(!this.allItems.length) {
this.reset();
this.busy = true;
return this.fetch(query)
.then(this.loadAllItems.bind(this)) //bind here
.then(this.loadVisibleItems.bind(this)) // here
.finally(function () {
this.busy = false;
}.bind(this)); //and here
But keep in mind - when you pass a function as a callback to a then or to forEach e.t.c it'll lose this context. So, use bind exactly when you pass the function which uses this syntax as a callback.

JavaScript's setTimeout doesn't work

I have a simple JS object which emulates traffic lights:
function TrafficLight(redTime, yellowTime, greenTime) {
var self = this;
this.__timer = null;
this.__state = null;
this.__redTime = redTime;
this.__yellowTime = yellowTime;
this.__greenTime = greenTime;
var setnewtimer = function (delay, func) {
console.log('SET!');
if (self.__timer) {
clearTimeout(this.__timer);
}
self.__timer = setTimeout(delay, func);
};
TrafficLight.prototype.toRed = function () {
this.__state = 'red';
setnewtimer(this.__redTime, function () {
console.log('RED!');
self.toGreen();
});
};
TrafficLight.prototype.toGreen = function () {
this.__state = 'green';
setnewtimer(this.__greenTime, function () {
console.log('GREEN');
self.toYellow();
});
};
TrafficLight.prototype.toYellow = function () {
this.__state = 'yellow';
setnewtimer(this.__yellowTime, function () {
console.log('YELLOW');
self.toRed();
});
};
TrafficLight.prototype.state = function () {
return this.__state;
};
this.toGreen();
}
But when I make a TrafficLight object (like var a = new TrafficLight(1000, 1000, 1000);), every a.state() call returns green (so traffic light doesn't change its state by timer. What's wrong with my code?
You don't call setTimeout correctly.
Change
setTimeout(delay, func);
to
setTimeout(func, delay);

Categories

Resources