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>
Related
I found this code, to add before and after the ability to run a function, searched and don't know, what is new(self) mean there?
Function.prototype.before = function(fn) {
const self = this
return function() {
fn.apply(new(self), arguments)
return self.apply(new(self), arguments)
}
}
Function.prototype.after = function(fn) {
const self = this
return function() {
self.apply(new(self), arguments)
return fn.apply(new(self), arguments)
}
}
...
const validate = function(){
// ...
}
const formSubmit = function() {
// ...
ajax( 'http:// xxx.com/login', param )
}
submitBtn.onclick = function() {
formSubmit.before( validate )
}
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();
}
In my application I generate a circle using the HTML element canvas.
The generation of the circle works well: the circle is correctly rendered.
The problem is that I have to put that circle in an option of a select, and as far as I know is not possible to put a canvas inside an option, therefore I probably have to convert the canvas to a base64 image so that I should be able to use it as a background-image of the option.
However, the conversion from canvas to base64 image is not working, as the browser is rendering a blank image.
I have created a fiddle for troubleshooting: https://jsfiddle.net/4hfmp0cs/
Here below you can see the javascript code of the fiddle.
function foo()
{
var circle = getStateCircle("opened");
//var gl = circle.getContext("webgl", { preserveDrawingBuffer: true });
var dataUrl = circle.toDataURL("image/png");
var img = document.createElement("img");
img.src = dataUrl;
document.getElementById("container").appendChild(img);
}
function getStateCircle(state)
{
var stateCircle;
if(state === "opened")
{
stateCircle = new Circle("#ffcc00", "20px");
}
else if(state === "accepted")
{
stateCircle = new Circle("#33cc33", "20px");
}
else if (state === "refused")
{
stateCircle = new Circle("#ff3300", "20px");
}
else if (state === "closed")
{
stateCircle = new Circle("black", "20px");
}
else
{
throw new Error("The state of the offer is unknown");
}
stateCircle.buildCircle();
var circle = stateCircle.getCircle();
return circle;
}
function Circle(color, size)
{
this._color = color;
this._size = size;
this._circle;
this.buildCircle = function()
{
var style = {
borderRadius: "50%",
backgroundColor: this._color,
height: this._size,
width: this._size
}
this._circle = new ElementBuilder("canvas").withStyleObject(style).getElement();
}
this.buildCircleAndAppendTo = function(father)
{
this._buildCircle();
father.appendChild(this._circle);
}
this.getCircle = function()
{
return this._circle;
}
}
function ElementBuilder(elementName) {
var This = this;
this.element = document.createElement(elementName);
this.withName = function (name)
{
this.element.setAttribute("name", name);
return this;
};
this.withAttribute = function (attributeName, attributeValue)
{
this.element.setAttribute(attributeName, attributeValue);
return this;
};
this.withId = function (id)
{
this.element.setAttribute("id", id);
return this;
}
this.withClass = function (className)
{
this.element.setAttribute("class", className);
return this;
}
this.addClass = function (className)
{
this.element.className = this.element.className + " " + className;
return this;
}
this.withTextContent = function (text)
{
this.element.textContent = text;
return this;
}
this.withValue = function (value)
{
this.element.value = value;
return this;
}
this.getElement = function ()
{
return this.element;
};
this.withChild = function (child)
{
this.element.appendChild(child);
return this;
};
this.withEventListener = function (type, func)
{
this.element.addEventListener(type, func);
return this;
};
this.withClickEventListener = function (func)
{
this.element.addEventListener("click", func);
return this;
}
this.withDoubleClickEventListener = function (func)
{
this.element.addEventListener("dblclick", func);
return this;
}
this.withStyle = function (styleAttribute, value)
{
this.element.style[styleAttribute] = value;
return this;
}
this.withStyleObject = function (styleObject)
{
ensureIsAnObject(styleObject);
var keys = Object.keys(styleObject);
keys.forEach(function (elt) {
This.withStyle(elt, styleObject[elt]);
});
return this;
}
}
function ensureIsAnObject(value, argumentName) {
if (!(typeof value == "object")) {
throw new Error("The argument '" + argumentName + "' should be an object, but it's type is --->" + typeof value);
}
}
The HTML code
<div id="container">
</div>
<button onclick="foo()">Append image</button>
Nowhere are you actually drawing to the canvas, just styling it with css, which is rendered separate from the canvas. You can replace the canvas with a div, or any other block element and just append that to the document to get the correct effect.
Converts canvas to an image
function convertCanvasToImage(canvas) {
var image = new Image();
image.src = canvas.toDataURL("image/png");
return image;
}
Or Check in
http://jsfiddle.net/simonsarris/vgmFN/
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);
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);