We are about to switch to a new forum software. Until then we have removed the registration on this forum.
I am coding like the below. There are a lot of clickable squares on the screen as the image at the bottom shows. I would like to know which one was clicked. But I can not get any value which was clicked from "myBlock[i].mousePressed(whichBlock);". I wonder how to give a value to the "whichBlock" function that I defined.
var myBlock=[];
var blockNumber=50;
function setup(){
for(var i=0;i<blockNumber;i++){
myBlock[i]=createDiv(i);
myBlock[i].size(30,30);
myBlock[i].position(random(windowWidth*0.8),random(windowHeight*0.8));
myBlock[i].style("background-color","#FAA");
myBlock[i].style("border","1px solid #444");
myBlock[i].mousePressed(whichBlock);
//this does not send the value which is mousePressed to the function below.
}
}
function whichBlock(n){
alert(n);
//myBlock[n].hide(); //I want to do this if possible.
}
Answers
https://developer.Mozilla.org/en-US/docs/Web/API/Event/target
Dear GoToLoop, Thats great!!! I could not manage it by myself, but I understand how it works now. Event.target.textContent would be very useful to distinguish it. Thank you so much.
@GoToLoop 's suggestion is a reasonable one as it keeps things fairly simple; though you may not want to store the array index as text in the element - in which case you could simply add it to a data attribute and retrieve it from there...
There is however a more elegant solution which involves using a closure - which allows you to pass a reference to your block directly to your callback function:
It has to be said that this is a JavaScript concept that often catches people out and can initially be difficult to get your head round; so you may prefer the previous solution...
Note also that the self-invoked function is something of a historical hack to get around JS only having function level scope. In ECMAScript2015 you can set block level scope using let.
Nice closured callback approach @blindfish! =D>
Only critique is that it stores 50 anonymous callbacks rather than re-using 1 only like the whichBlock().
Excellent idea! By chance blocks' indices happen to be exactly the corresponding Element's innerHTML.
But for most cases that may not be true. Store them in _data-*_ via attribute() is more adequate:
block.attribute('data-idx', i);
Then use HTMLElement's dataset property to access them and we're done: :-bd
blocks[evt.target.dataset.idx].hide();
Online link: http://p5js.ProcessingTogether.com/sp/pad/iframe/ro.CkNQ4FRp3hEzHn/latest
Hello again, Both of you @blindfish & @GoToLoop are wonderful!
The ideas are almost out of my scope.
For myself, I did a little improvement referring to @blindfish's suggestion about using data attribute.
For this time, I do not want to show the index numbers on the squares, which means a case of empty innerHTML/innerTEXT.
This is good enough for me. But I did another code which is like this. It might be a bit strange way...
It works fine but I do not know why. Does it make sense?
The point was I wanted to use invisible values to identify them.
Thank you so much. It helped me a lot anyway.
Nice finding! Keyword
this
typically points to the object which had invoked thefunction
: <:-Phttps://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
For the mousePressed()'s callback, its
this
is the p5.Element object which createDiv() returns:http://p5js.org/reference/#/p5.Element
For a callback like this:
function whichBlock(evt) {}
, if we have the following statement there:alert(this.elt == evt.target);
, it's gonna popuptrue
. :-Bp5.Element's property elt points to the actual HTMLElement wrapped by it:
http://p5js.org/reference/#/p5.Element/elt
In turn, Event's property target coincidentally points to that same HTMLElement too. $-)
Therefore we just need
this
rather thanblocks[evt.target.dataset.idx]
or evenblocks[this.value()]
.Actually we don't even need the blocks[] array. Neither value() nor attribute(). Only
this
! *-:)Here's latest tweaked version 2.0, which replaced everything by mere
this.hide();
! \m/And b/c there's no blocks[] anymore, randomlyPlaceBlocks()'s
for..of
loop is accessing the "secret" p5's property _elements as its replacement:for (let b of _elements) {
:ar!Again it's the same online link: http://p5js.ProcessingTogether.com/sp/pad/iframe/ro.CkNQ4FRp3hEzHn/latest
@GoToLoop IMHO it's dangerous to rely on 'happy accidents' like this: I don't see anything in the documentation to suggest you can rely on 'this' to be a reference to the elt. As such there's the risk that a change to the library breaks your code. Likewise referencing
_elements
is a bad idea: that leading underscore is a convention to suggest this is a private property: what happens if the p5 devs decide to make their privacy more robust?Re: the closure code I used above: it demonstrated a well established approach (i.e. it's not my original idea!) to solving scope issues and passing parameters to functions from callbacks.
The point about storing 50 anonymous callbacks is perfectly valid. In a general web context that was never going to be an issue and if the callback does anything remotely heavy you'd simply use it to invoke a separate function that is defined once. Obviously in a canvas context where you might be invoking 1000s of objects it might become a real concern...
Amended code using
let
- instead of a self-invoking function - to invoke block level scope:In order to avoid any confusion, lemme repeat that elt is a property of wrapper type p5.Element which points to the actual underlying HTMLElement object: http://p5js.org/reference/#/p5.Element/elt
Callback hideP5Element() is assuming that its
this
is of datatype p5.Element rather than the actual HTMLElement. HTMLElement would be evt.target, where evt is callback's parameter.Actually by default,
this
would be evt.target instead of p5.Element. But an internalfunction
within "p5.Element.js" source called attachListener() applies bind() to all of our passed EventListener callback:Here's mousePressed() method which calls attachListener():
Therefore,
this
is guaranteed to be of type p5.Element as long attachListener() applies bind() upon its fxn parameter.IMO, bind() isn't necessary. And it's instantiating wrapper bound functions over our own callbacks.
As mentioned, w/o bind(),
this
would be of type HTMLElement instead.Read about "The value of this within the handler" below for more indepth info about it:
https://developer.Mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#The_value_of_this_within_the_handler
Indeed I've made a potential buggy mistake, which noCanvas() is by chance overshadowing it. b-(
"Private" _elements[] property stores all p5.Element objects, including even the canvas! @-)
Since my sketch only got createDiv() + noCanvas(), _elements[] got exactly 50 p5.Element objects.
But we shouldn't assume as such, b/c if we add more things to the program, we may end up introducing more p5.Element objects. :-SS
Therefore, if we wanna rely on _elements[], we gotta "label" those p5.Element object groups we need to iterate over. For such, we can use class() or addClass() methods: *-:)
Already applied those fixes to my sketch v2.1 above. :bz
Unless it is a documented feature of the API there is no guarantee that it won't change in future. I understand the desire to understand how the library works internally; but if you want to write sustainable code then you should limit yourself to the documented API. You should also avoid exploiting 'happy accidents' like these; particularly if doing so is dependent on adding hacks like adding class(LABEL) 8-X
Due to the experimental nature of p5.js project nothing is guaranteed there; be it documented or internal implementation! :ar!
After all, it's still version 0.51 currently. That means it hasn't reached the "gold" 1.0 stable state. :-\"
As long as
var f = fxn.bind(ctx);
statement stays that way,this
means p5.Element. :PSpecifying a className for some group of Element objects is the default procedure for CSS styling.
That is, some specific styling can be applied to a whole "labeled" group everywhere.
The opposite is the Element's id, which is unique by web standards:
BtW, _elements[] + class() check above can be replaced by selectAll(): *-:)
http://p5js.org/reference/#/p5/selectAll
I take your point on the beta nature of p5, but that's even more reason to stick to the documented path, rather than straying into the jungle ;)
...but you're using a style attribute to store state: nasty. Of course in code under your control you can get away with it, but it's still bad practice :P
If you really must store state on elements use data attributes ;)
I've already done that in my version 1.01@ line #30:
.attribute('data-idx', i)
.But those were for individual label values, not "groupie". :P
It's neither style() nor attribute() but class()! [-X
They go into both Element's properties className & classList. :-B
Statement
createDiv('').class(LABEL);
is analogous to<div class=clickable-block></div>
btW. ;;)You misunderstood: 'class' is an attribute (of an html tag) for storing style information, so is (technically) inappropriate for state data. I didn't mean the style attribute ;)
Both class & id HTML tag attributes (not style 1s) are mainly used as CSS selectors for styling purposes.
But in no way those attributes are exclusive for CSS code. [-(
JS also got access to them via document global variable. Some examples:
Labeling and querying elements w/ id or class is as old as HTML. Be it for CSS or JS or both. >-)
I don't dispute that; but you're talking about explicitly storing state in a presentational attribute. Experienced front end devs will tell you that this is bad practice that will lead to brittle code; even though most of us will have been guilty of doing it ;)
That "state" is stored right after the creation time:
createDiv('').class(LABEL);
It's a fixed label and it's not supposed to be mutable at all later on.
On the other hand, position(), hide() & show() are the mutable state actions within the sketch's logic.
It's questionable whether class & id attributes are presentational or not.
If we replace those 2 style() methods w/ some ".css" file, we'd surely need to label them w/ class().
So some unified style would be applied to all of those
<div>
from the CSS code.Within the JS sketch, the class attribute is used to distinguish which elements to mutate their position and visibility rather their other styles.
But somehow you insist my usage of class inside JS is some kinda hack.
While I'm sure you'd laud class for exclusive CSS code usage. 8-|
This is just 99% self-contained programmatic JS code + p5.js library.
Rather than the traditional trio: HTML + CSS + JS. :P
You still fail to understand my point. As I've implied, whilst using class to store state is considered bad practice (especially by MVC advocates) it is something that does happen. But your use of it in this context is a hack to get around the fact that your inappropriate reference to
_elements
has already led you into trouble; because it contains elements you don't wish to manipulate.Even accepting the use of the class hack to identify the elements you're interested in, the approach is inefficient: if
_elements
stores references to all elements created in the sketch you may land up iterating over any number of elements that aren't relevant. All that to save you having to explicitly store references to them in an array under your control... :(|)I've stated very clearly that in my particular sketch _elements cause no problems at all, since I was using noCanvas() from the very beginning, while using more conservative approaches. See my version 1.01.
A short after, I've realized _elements would get buggy for other regular sketches, since the canvas itself is tracked by _elements too.
Nevertheless, I've already posted another approach which replaces _elements by selectAll():
for (let b of selectAll('.' + LABEL)) {
It still relies on canvas HTML tag attribute for query selection, just like any CSS would.
And that's my point: 100% JS programmatic approach solutions are as valid as CSS 1s.
And they're not inappropriate, and neither labeling is some kinda state! ~O)
That's true! Having some container w/ the actual objects we're interested in is clearly more efficient than iterating over a general pool.
And that's exactly what I did in my version 1 series w/
const blocks = Array(BLOCKS)
.Version 2 series is more like a challenge at programming a sketch which can reference objects via more unusual means like
this
, _elements, selectAll(), etc.In short, series 1 are for my conservative approaches. Series 2 are my bold advanced 1s. \m/
On the other hand, all CSS selectors demand a search throughout the whole page in order to find out the id and className attributes where it needs to apply the appropriate styles.
In my series 2 I do the same, but for position and visibility. Which are also some sorta style too! #:-S
I still find that accusation bullocks! I'm tagging/labeling a whole group of
<div>
elements w/ 'clickable-block', in order to query them later for position + visibility styling.If I had done some batch of
<div class=clickable-block></div>
inside the HTML and used CSS for positioning + visibility as well I'm sure you wouldn't imply thatclass=clickable-block
is putting some kinda "state" over them all. ~:>In both JS & HTML cases, the labeling is permanent, they don't mutate later at all.
You'd be right if I was using method class() to mutate 'on' & 'off' when the element is clicked at.
Or even to store the # of times it's been clicked at! X_X
In those mutable cases, the data-* properties should be used instead.
But clearly my usage for class() or selectAll() are only for querying purposes inside randomlyPlaceBlocks()'s loop. We can't use querying functions for data-* properties after all. :-@
young male cows? :))
bad practice doesn't mean don't use it. It just means you should use with caution and avoid if possible; particularly in large scale projects.
This article does a better job of justifying my recommendation ;)
Seems like you're not paying attention to my replies that well. :-&
I've clearly mentioned that 'clickable-block' is a permanent label/tag/tattoo which is set at creation time and doesn't go away. And they're used for element querying selection only.
I've also mentioned bad stateful examples like mutating 'on' and 'off' labels, increasing the label as a counter for how many times the element was clicked and such.
Now you post a link to an article which mentions toggling the 'clickable' label as bad.
Which is clearly right. But doesn't represent my immutable & correct usage of the label at all! (:|
Perhaps I haven't been especially clear that the problem here is the context in which you're trying to justify use of classes as 'labels'.
IF the class was embedded on elements in the original HTML it would make sense to retrieve them with a DOM query referencing the common class; but you're doing this on elements that are being dynamically added by your own client-side script. In that context it makes no sense to dynamically add a class so you can later use this to query the DOM and retrieve the elements you're creating. You might want to dynamically add classes to apply styling; but, if you later need to reference the elements from within the same script, anything other than storing a direct reference to them at creation time strikes me as baffling and incorrect.
That's exactly the prejudice point I've been exposing about a couple of times:
I understand your point that I coulda simply tracked those elements via containers in JS. That's what my version series 1 does after all.
However I can't accept either that opting to query any DOM element, no matter the language which were used to create them, is somehow wrong! [-X
That's very prejudice doncha think? ;;)