How to access the outer this from jQuery functions? - javascript

out of curiosity is there a way to access this.color from the paint function?
function Foo(color)
{
this.color = color;
this.paint = function paint()
{
$("select").each(function(idx, el)
{
$(el).css("background", color); // OK
// $(el).css("background", this.color); // this.color is undefined
})
}
}
new Foo("red").paint();
Thanks

var that = this;
function (idx, el) {
// access what used to be this.color as that.color
}

Related

I can't reset the image on JavaScript

I can't reset the image on javascript, codepen say imagem in not defined.
Codepen link: https://codepen.io/AlissonTelez/pen/ExPJBvv
Thats variables:
var originalImage;
var grayImage = null;
var redImage = null;
var rainbowImage = null;
var windowImage = null;
var canvas = document.getElementById("can1");
thats function to make filter red:
function makeRedy() {
if (imgCarregada(imagemRedy)) {
filtroVermelho();
var canNovo = document.getElementById("can");
imagemRedy.drawTo(canvas);
}
}
function filtroVermelho() {
for (var pixel2 of imagemRedy.values()) {
avg2 = (pixel2.getRed()+pixel2.getGreen()+pixel2.getBlue()/3);
if (avg2 < 128) {
pixel2.setRed(2*avg2);
pixel2.setGreen(0);
pixel2.setBlue(0);
}
else {
pixel2.setRed(255);
pixel2.setGreen((2*avg2)-255);
pixel2.setBlue((2*avg2)-255);
}
}
}
that's reset function:
function resete() {
if (imgCarregada(imagem)) {
imagem.drawTo(canvas);
}
}
function imgCarregada(x) {
if (x === null) {
return false;
}
else if (x !== null) {
return true;
}
}
You have not defined imagem. But it appears your original image is being stored as imagemOriginal.
Change your resete() function to this;
function resete() {
if (imgCarregada(imagemOriginal)) {
imagemOriginal.drawTo(canvas);
}
}
Based on your CodePen. Which appears to hold different values to what you pasted in your question.
read my comments in code and try understand what's new
// for checking later if null or not
var imagemOriginal = null;
var imagemRedy = null;
var imagemGray = null;
var avg;
var avg2;
// ctx for canvas api 2d
var canvas , ctx;
function upload() {
var img = document.getElementById("Finput");
canvas = document.getElementById("can");
// defined canvas context 2d for drawing and accessing all canvas api
ctx = canvas.getContext("2d");
imagemOriginal = new SimpleImage(img);
imagemGray = new SimpleImage(img);
imagemRedy = new SimpleImage(img);
imagemOriginal.drawTo(canvas);
}
// calling upload as first step for making canvas ready
upload();
function makeGray() {
if (imgCarregada(imagemGray)) {
filtroCinza();
var canNovo = document.getElementById("can");
imagemGray.drawTo(canvas);
}
}
function filtroCinza() {
for (var pixel of imagemGray.values()) {
avg = (pixel.getRed()+pixel.getGreen()+pixel.getBlue())/3;
pixel.setRed(avg);
pixel.setGreen(avg);
pixel.setBlue(avg);
}
}
function makeRedy() {
if (imgCarregada(imagemRedy)) {
filtroVermelho();
var canNovo = document.getElementById("can");
imagemRedy.drawTo(canvas);
}
}
function filtroVermelho() {
for (var pixel2 of imagemRedy.values()) {
avg2 = (pixel2.getRed()+pixel2.getGreen()+pixel2.getBlue()/3);
if (avg2 < 128) {
pixel2.setRed(2*avg2);
pixel2.setGreen(0);
pixel2.setBlue(0);
}
else {
pixel2.setRed(255);
pixel2.setGreen((2*avg2)-255);
pixel2.setBlue((2*avg2)-255);
}
}
}
// in resete function you put arguments not exist 'imagem' ! and that give you error
// soo sloution is checking 'imagemOriginal'
function resete() {
// soo here do your check // if true call restCanvas for make canvas clear :)
if (imgCarregada(imagemOriginal)) resetCanvas();
}
// this function using 'ctx' or canvas context for make canvas clean
function resetCanvas(){
// fillstyle for select your draw color
ctx.fillStyle = "white";
// fillrect for draw with resloution :)
ctx.fillRect(0,0,canvas.width,canvas.height);
}
// here just syntax better :)
function imgCarregada(x) {
if (x === null) return false;
else if (x !== null) return true;
}

How to pass unspecified number of parameters to setInterval

I need to create an interval wrapper to track if it has been cleared.
The number of parameters to pass to the interval callback should be variable. So this is the code (not working) I implemented to test it:
function MyInterval() {
var id = setInterval.apply(this, arguments); // NOT VALID!!
this.cleared = false;
this.clear = function() {
this.cleared = true;
clearInterval(id);
};
}
var x = 2;
var y = 3;
var fn = function() {
x = x + y;
console.log(x);
};
var interval = new MyInterval(fn, 5000, x, y);
Within the call to setInterval, this must refer to the global object, so instead of this, you want window in your constructor:
var id = setInterval.apply(window, arguments);
// here -------------------^
(or in loose mode you could use undefined or null.)
Then it works, at least on browsers where setInterval is a real JavaScript function and therefore has apply:
function MyInterval() {
var id = setInterval.apply(window, arguments);
this.cleared = false;
this.clear = function() {
this.cleared = true;
clearInterval(id);
};
}
var x = 2;
var y = 3;
var fn = function() {
x = x + y;
log(x);
};
var interval = new MyInterval(fn, 500, x, y);
setTimeout(function() {
interval.clear();
}, 3000);
function log(msg) {
var p = document.createElement('p');
p.appendChild(document.createTextNode(msg));
document.body.appendChild(p);
}
Note, though, that host-provided functions are only required to be callable, they are not required to inherit from Function.prototype and so they're not required/guaranteed to have apply. Modern browsers ensure they do, but earlier ones (IE8, for instance) did not. I can't speak to how well-supported apply is on setInterval.
If you need to support browsers that may not have it, just to use your own function:
function MyInterval(handler, interval) {
var args = Array.prototype.slice.call(arguments, 2);
var tick = function() {
handler.apply(undefined, args);
};
var id = setInterval(tick, interval);
this.cleared = false;
this.clear = function() {
this.cleared = true;
clearInterval(id);
};
}
This also has the advantage that it works even on browsers that don't support additional args on setInterval (fairly old ones).
Example:
function MyInterval(handler, interval) {
var args = Array.prototype.slice.call(arguments, 2);
var tick = function() {
handler.apply(undefined, args);
};
var id = setInterval(tick, interval);
this.cleared = false;
this.clear = function() {
this.cleared = true;
clearInterval(id);
};
}
var x = 2;
var y = 3;
var fn = function() {
x = x + y;
log(x);
};
var interval = new MyInterval(fn, 500, x, y);
setTimeout(function() {
interval.clear();
}, 3000);
function log(msg) {
var p = document.createElement('p');
p.appendChild(document.createTextNode(msg));
document.body.appendChild(p);
}
You might be tempted to use the new ES2015 spread operator:
var id = setInterval(...arguments);
...but note that if you transpile (and right now you'd have to), it ends up being an apply call, and so you have the issue of whether apply is supported.
I suggest that you pass an "options" parameter to your timeout.
var MyInterval = (function(window) {
return function(callbackFn, timeout, options) {
var id = setInterval.apply(window, arguments);
this.cleared = false;
this.clear = function() {
this.cleared = true;
clearInterval(id);
};
}
}(window));
var fn = function(opts) {
opts.x += opts.y;
console.log('x = ', opts.x);
};
var opts = {
x: 2,
y: 3
};
var ms = 5000;
var interval = new MyInterval(fn, ms, opts);
// Bootstrap a custom logger. :)
console.log = function() {
var logger = document.getElementById('logger');
var el = document.createElement('LI');
el.innerHTML = [].join.call(arguments, ' ');
logger.appendChild(el);
logger.scrollTop = logger.scrollHeight;
}
body{background:#7F7F7F;}h1{background:#D7D7D7;margin-bottom:0;padding:0.15em;border-bottom:thin solid #AAA;color:#444}#logger{height:120px;margin-top:0;margin-left:0;padding-left:0;overflow:scroll;max-width:100%!important;overflow-x:hidden!important;font-family:monospace;background:#CCC}#logger li{list-style:none;counter-increment:step-counter;padding:.1em;border-bottom:thin solid #E7E7E7;background:#FFF}#logger li:nth-child(odd){background:#F7F7F7}#logger li::before{content:counter(step-counter);display:inline-block;width:1.4em;margin-right:.5em;padding:.25em .75em;font-size:1em;text-align:right;background-color:#E7E7E7;color:#6A6A6A;font-weight:700}
<h1>Custom HTML Logger</h1><ol id="logger"></ol>
I created a utility function rather than a constructor to solve your issue.
function Wrapper(delay) {
var isCleared,
intervalId,
intervalDelay = delay || 5e3; // default delay of 5 sec
function clear() {
if (!isCleared) {
console.log('clearing interval');
isCleared = true;
clearInterval(intervalId);
}
}
function setUpInterval(callback){
var params = [].slice.call(arguments, 1);
if (!callback) {
throw new Error('Callback for interval expected');
}
params.unshift(intervalDelay);
params.unshift(callback);
intervalId = setInterval.apply(null, params);
}
return {
setUp : setUpInterval,
clear : clear
}
}
function intervalCallback() {
console.log([].slice.call(arguments).join(','));
}
var wrapper = Wrapper(1e3); // create wrapper with delay for interval
console.log('test case 1');
wrapper.setUp(intervalCallback, 'params', 'to', 'callback');
// call clear interval after 10sec
setTimeout(function() {
wrapper.clear();
}, 10e3);
Hope this helps.

Javascript, Calling functions from another onclick in object constructor

I need help calling the this.load function from inside the thediv.onclick. I stripped out most of the code so its really basic, but I really can't find a way to do it. Here is what I currently have:
function CreatePizza(Name, Toppings) {
this.n = Name;
this.t = Toppings;
this.load = function loadpizza() {
//function that i want to be called
}
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
// Call this.load function here
}
}
}
The problem is that inside the onclick handler, this will refer to the <div>, not the other this which you refer to repeatedly.
Two possible solutions:
Save a reference to your desired this:
that = this;
thediv.onclick = function () {
that.load()
};
Bind this to your function:
thediv.onclick = function () {
this.load();
}.bind(this);
Or, if that's the only thing you're doing in that function anyway:
thediv.onclick = this.load.bind(this);
Because of closures, you can simply assign this to a variable and call it from that!
function CreatePizza(Name, Toppings) {
var self = this;
this.n = Name;
this.t = Toppings;
this.load = function loadpizza() {
//function that i want to be called
}
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
self.load();
}
}
}
I would like to mention that a nicer - and not necessarily better, before anyone starts a flamewar - way to attach events to your div (and more elegant in my opinion) is to use thediv.addEventListener('click', self.load, false). Just a side-note, though.
Backup the this object before binding the event.
this.create = function button() {
var that = this,
thediv = document.createElement("div");
thediv.onclick = function() {
// Call this.load function here
that.load();
}
}
function CreatePizza(Name, Toppings) {
this.n = Name;
this.t = Toppings;
var foo = function loadpizza() {
//function that i want to be called
};
this.load = foo;
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
foo();
}
}
}
function CreatePizza(Name, Toppings) {
this.n = Name;
this.t = Toppings;
this.load = function loadpizza() {
//function that i want to be called
}
var self = this;
this.create = function button() {
var thediv = document.createElement("div");
thediv.onclick = function() {
self.load()
}
}
}

Pass object variables into eventlistener

While trying to create a mouse listener to canvas object I faced a problem which took me a long time to solve - How can I pass object variables (this.X, this.Y) to an event listener, for example:
function Test() {
this.canvas = ....
this.mouseDownHandler = ....
canvas.addEventLIstener('mousedown', this.mouseDownListener, false);
}
So I came up with the following solution
This is the solution the worked for me -
function Test() {
this.ctx = this.canvas.getContext("2d");
var self = this;
this.canvas.addEventListener("mousedown",
function(e, param) {
self.mouseDownHandler(e, param);
}.bind(null, this), false);
}
Test.prototype.mouseDownHandler = function(t, e) {
t.ctx.fillRect(e.pageX, e.pageY, 10, 10);
};
If you don't mind me slightly simplifying #Yehonatan 's answer:
class Test {
constructor() {
this.canvas = document.getElementById("app")
this.ctx = this.canvas.getContext("2d")
this.canvas.addEventListener("mousedown", e => this.mouseDownHandler(e))
}
mouseDownHandler(e) {
this.ctx.fillRect(e.pageX, e.pageY, 10, 10)
}
}
let t = new Test()
JSFiddle example

Cant' change an object variable from an outer event

I was playing around with making game in JS. And hit a brick wall which is inability to change a variable from an event in the main html file. Namely speaking offSetX. Why it doesn't change?
var game = new Game();
window.addEventListener("keyup", game.input);
game.start('myCanvas');
The game object looks like this:
function Game() {
this.offSetX = 0;
this.init = function (id) {
this.canvas = document.getElementById(id);
this.context = this.canvas.getContext('2d');
this.blocks = [];
this.blocks.push(new block());
};
this.logic = function () {
for (var i in this.blocks) {
this.blocks[i].update(this.offSetX);
}
};
this.draw = function () {
for (var i in this.blocks) {
this.blocks[i].draw(this.context);
}
};
this.main = function () {
this.logic();
this.draw();
console.log(this.offSetX);
};
this.input = function (key) {
if (key.keyCode == 37) {
this.offSetX--;
console.log(this.offSetX);
}
if (key.keyCode == 39) {
this.offSetX++;
console.log(this.offSetX);
}
};
this.start = function (id) {
var _this = this;
this.init(id);
this.interval = setInterval(function () {
_this.canvas.width = _this.canvas.width;
_this.main();
}, 30);
}
};
Try this:
window.addEventListener("keyup", function(key){
game.input.apply(game,[key]);
});
The problem was by window.addEventListener("keyup", game.input) line , you are adding handler for window object, that's why in input method , "this" is window object(which does not have any "offSetX" method), not the game object.

Categories

Resources