Here's what I thought was simple code:
<html>
<head>
<script>
function Foobar(id) {
self = this;
self.id = id;
self.canvas = document.createElement('canvas');
self.canvas.style.border = '1px solid black';
document.body.appendChild(self.canvas);
self.canvas.addEventListener('mousedown', self.onMouseDown, true);
self.onMouseDown = function(e) {
console.log(self.id);
}
}
var s1, s2;
function onLoad() {
s1 = new Foobar(1);
s2 = new Foobar(2);
}
</script>
</head>
<body onload='onLoad()'>
</body>
</html>
Why does the console not pop up with the id number?
Here's a fiddle: http://jsfiddle.net/VRn7v/
You must assign the method on self before binding the event listner
FIXED
http://jsfiddle.net/landau/VRn7v/3/
self is a property of the window object and is generally a bad variable name. Also, your self variable is an implicit global because you are missing the var keyword. Finally, you are binding the event handler before it is declared.
function Foobar(id) {
var that = this; // pick a better name, and use "var"
that.id = id;
that.canvas = document.createElement('canvas');
that.canvas.style.border = '1px solid black';
document.body.appendChild(that.canvas);
that.onMouseDown = function(e) {
console.log(self.id);
}; // missing semicolon
// bind the handler after declaring it
that.canvas.addEventListener('mousedown', that.onMouseDown, true);
}
self.canvas.addEventListener('mousedown', self.onMouseDown, true);
In this line, self.onMouseDown is undefined, because you assign it only afterwards. Possible quickfixes to get it work:
move the function creation / assignment above the usage
move the onMouseDown function to the prototype (bad, no local self in the scope)
don't use self.onMouseDown, but self.canvas.onmousedown (cross-browser-safe with traditional event registration)
Also, your self variable is global. With a working handler attachment, both clicks will log "2". And, the self variable is not needed in the most places you use it - the only need for it is in the event handler.
Corrected fiddle: http://jsfiddle.net/VRn7v/2/
Related
I've had this error message "TypeError: a is undefined" for a while and I finally narrowed it down to a few lines of code in my project. I've created a smaller project from the original to demonstrate my problem:
new p5();
var particle = function(X, Y, C, Kind, Fun) {
this.pos = createVector(X, Y);
this.vel = createVector(0, 0);
this.accel = createVector(0, 0);
this.col = C;
this.isDead = false;
this.kind = Kind;
this.affect = Fun;
};
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
};
var particles = new Array(new particle(1, 1, color(1, 1, 1), function () {
cosole.log("your mamma");
}));
function draw() {
};
If I run this in a browser, I get the error message mentioned above. In case you were wondering what my html looks like, here it is:
<html>
<head>
<meta charset="utf-8">
<title>Test</title>
<script src="p5.min.js"></script>
<script src="p5_test.js"></script>
</head>
<style>
body {margin: none; padding: none;}
</style>
<body></body>
</html>
Any suggestions??
It looks like you're trying to use on-demand global mode to get around the restriction of not being able to use P5 functions until after setup() is called. I would expect your code to work.
However, when I run the code, I get this error:
Uncaught TypeError: Cannot read property '_colorMode' of undefined
at new p5.Color (p5.js:6482)
at p5.color (p5.js:6174)
at sketch.js:17
You're using the minified version of the library, so variable names aren't really useful. I'm using the unminified version, so I can see that the actual problem is that the _colorMode variable can't be found.
Tracing through the code, we see that your code calls the color() function, which hits line 6174 of P5.js:
return new p5.Color(this._renderer, arguments);
Which calls the p5.Color constructor, which hits line 6482 of P5.js:
this.mode = renderer._colorMode;
From this, we can see that the renderer variable is undefined, and when we try to access its _colorMode variable, we get the error.
So, it looks like P5's internal renderer variable is not defined, even though you're using on-demand global mode. The on-demand global mode gives you access to simple functions like random(), but it doesn't give you access to functions that require a renderer to have been initialized.
To fix your problem, just move your initialization into your setup() function:
var particle = function(C) {
this.col = C;
};
var particle;
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
particle = new particle(color(1, 1, 1));
}
function draw() {
}
Note that you can also get rid of the new p5() at the top of your code, since it wasn't really helping you at all. Also note that I've simplified your code for the purpose of creating a MCVE, but the same ideas should hold true for your main project.
This is jscript over Lightswitch HTML Client
Same routine Works fine called from a Button, but fails when called from a Canvas event, showing an undefined data error.
// Creates a Canvas and add an eventlistner Works fine
myapp.AddEditLito.Montajes_render = function (element, contentItem) {
var itemTemplate = $("<canvas id='myCanvas' width='600' height='150'></canvas>");
var canvas = myCanvas;
myCanvas.addEventListener('mousedown', function (event) {
var context = canvas.getContext('2d');
DibTriangulo(screen, 50); // Draws a triangle, everything fine until here
// here is the problem,
CalcPrecio(screen, 1); // breaks with "Undefined Data.. etc."
}, false);
};
// Called from a Button, same routine works perfect!!
myapp.AddEditLito.Button1_execute = function (screen) {
CalcPrecio(screen, 1); // Works fine
};
// This routine Works perfect called from a button, but fails when called from a Canvas event!
function CalcPrecio(screen, col) {
$.each(screen.Liquidas.data, function (i, p) {
p.valor = p.cantidad * p.unitario;
});
};
//What I´m doing wrong? Heeeelp!
This problem is a result of calling your DibTriangulo and CalcPrecio routines from your canvas' mousedown event handler with just screen as the first parameter.
In your mousedown event handler and the _render closure it's defined in, the screen object is not specifically set to refer to the LightSwitch screen instance and instead simply refers to the window.screen object.
To resolve this you can simply change your code to use contentItem.screen as shown in the following revised _render method:
myapp.AddEditLito.Montajes_render = function (element, contentItem) {
var itemTemplate = $("<canvas id='myCanvas' width='600' height='150'></canvas>");
var canvas = myCanvas;
myCanvas.addEventListener('mousedown', function (event) {
var context = canvas.getContext('2d');
DibTriangulo(contentItem.screen, 50); // Changed to pass in contentItem.screen
CalcPrecio(contentItem.screen, 1); // Changed to pass in contentItem.screen
}, false);
};
The reason using just screen works in your button's _execute method is because the _execute method receives the LightSwitch screen object as its parameter (which you then use when calling your CalcPrecio method).
Let's say I have three Views. AppView, MenuView and StripView. MenuView contains multiple StripViews and AppView contains one MenuView. How can I trigger event from StripView and listen on that event on AppView.
EDIT
Let's say I want to click on ImageSurface on StripView and reigster that event on AppView, and then do some transitionig.
MY SOLUTION
Everything is based on Timbre app, created in Famou.us Starter Kit Reference Tutorials
// StripView.js (_setListeners() is called in StripView constructor and bodySurface is defined in code)
function _setListeners() {
var eventScope = this._eventOutput;
this.backgroundSurface.on('click',function(){
eventScope.emit('test', { somedata:'some value'} );
}.bind(this));
}
// MenuView.js (_createStripViews() is called in MenuView constructor)
function _createStripViews() {
this.stripModifiers = [];
var yOffset = this.options.topOffset;
for (var i = 0; i < this.options.stripData.length; i++) {
var stripView = new StripView({
iconUrl: this.options.stripData[i].iconUrl,
title: this.options.stripData[i].title
});
var stripModifier = new StateModifier({
transform: Transform.translate(0, yOffset, 0)
});
this.stripModifiers.push(stripModifier);
this.add(stripModifier).add(stripView);
yOffset += this.options.stripOffset;
stripView.pipe(this._eventOutput);
}
}
//AppView.js (menuView is defined in code and _setListeners() is called in AppView constructor)
function _setListeners() {
this.menuView.on('test',function(){
console.log("IT WORKS");
}.bind(this));
}
You want to use Views built in handlers to achieve this. These are _eventInput and _eventOutput.. Here is an example using an ImageSurface in StripView and responding to a click in AppView..
Hope it helps!
// In StripView.js
var imageSurface = new ImageSurface();
imageSurface.on('click',function(){
this._eventOutput.trigger('image-click', { somedata:'some value'} );
}.bind(this));
// In AppView.js
var stripView = new StripView();
this.subscribe(stripView);
this._eventInput.on('image-click',function(data){
// Do Something
});
I'm trying to add an event for all elements with "p" tag.
But instead of adding an event script colors all links in red
<script>
//create links
var code = ""
for (i=0;i<10;i++){
code += "<p><a href='#'>Link " + i + "</a></p>"
}
document.getElementById('links').innerHTML = code;
//add Events
for(i=0;i<document.getElementsByTagName("p").length;i++){
document.getElementsByTagName("p")[i].onmouseover = document.getElementsByTagName("p")[i].childNodes[0].style.color="green"
document.getElementsByTagName("p")[i].onmouseout = document.getElementsByTagName("p")[i].childNodes[0].style.color="red"
}
}
</script>
There is My code
Event handlers need to be functions. So you need something like this:
document.getElementsByTagName("p")[i].onmouseover = function() {
// You don't want to use i in a function in a loop since i will
// be different by the time the function gets called
// this is document.getElementsByTagName("p")[i]
this.childNodes[0].style.color="green"
}
You should probably also create the nodeList for the <p> tags outside of the loop so you're not traversing the DOM each time.
var paras = document.getElementsByTagName('p');
for(i=0;i<paras.length;i++){
paras[i].onmouseover = function() { /* */ };
paras[i].onmouseout = function() { /* */ };
}
I dont know why the blur method is not receiving the passed data when using on and a selector.
Here is the code, just foucs any input, and press tab, and you will see that the isTab parameter is not being setted. Why?
Fiddle:
http://jsfiddle.net/fz5yA/9/
Html:
<div id="Container">
</div>
JavaScript:
var Container = $("#Container");
Container.on({
keydown : function(e){
var Item = $(this);
console.log(e.keyCode);
if(e.keyCode == 9) {
Item.trigger("blur",[true]);
return false;
}
},
blur : function(e, isTab)
{
console.log("IsTab:" + isTab);
}},".Item");
for(var i = 0 ; i < 10 ; i++)
{
Container.append("<input class='Item'/>");
Container.append("<br/>");
}
The system generated blur event just has one argument, not two. There is no isTab argument passed to that event handler in the normal way that the system creates that event. You can see that in the jQuery doc.
If you change the event name of your manually triggered event to something that isn't the normal blur event and you fix the way you are passing the argument in .trigger(), then the extra argument will work. Change to this and you will see the extra argument:
Container.on({
keydown : function(e){
var Item = $(this);
console.log(e.keyCode);
if(e.keyCode == 9) {
console.log("aaa");
Item.trigger("myEvent", true); // Changed event name and fixed the syntax in this line
return false;
}
},
myEvent : function(e, isTab) // Changed event name here
{
console.log("IsTab:" + isTab);
}},".Item");
Demo here: http://jsfiddle.net/jfriend00/fz5yA/17/