How can i determine in which box i clicked? - javascript

First off i'm new to javascript and still learning its basics, i'm trying to determine in which box i clicked(canvas).
My boxes are a list of dictionaries that look like this if we use console.log to visualize them, let's call that list labels:
[
{"id":"1","image":"1-0.png","name":"","xMax":"4802","xMin":"4770","yMax":"156","yMin":"141"},
{"id":"2","image":"1-0.png","name":"","xMax":"4895","xMin":"4810","yMax":"157","yMin":"141"},
{"id":"3","image":"1-0.png","name":"","xMax":"4923","xMin":"4903","yMax":"156","yMin":"145"},
{"id":"4","image":"1-0.png","name":"","xMax":"4956","xMin":"4931","yMax":"156","yMin":"145"}
]
Here we can see we have 4 rectangles and their coordinates.
The function i used to get mouse clicks is :
canvas.addEventListener("contextmenu", getPosition, false);
function getPosition(event) {
event.preventDefault();
var x = event.x;
var y = event.y;
var canvas = document.getElementById("canvas");
x -= canvas.offsetLeft;
y -= canvas.offsetTop;
console.log("x:" + x + " y:" + y);
}
The part where i'm struggling is to find out if where i clicked are inside any of the boxes and if the click is inside one i want the id.
What i tried:
I tried adding this after the console.log in the previous code snippet:
for (i = 0,i < labels.length; i++) {
if (x>labels[i].xMin) and (x<labels[i].xMax) and (y>labels[i].yMin) and (y<labels[i].yMax) {
log.console(labels[i].id)
}
}
but it didn't work

The rects in Labels are all very far to the right, so probably you need to add the scroll position to the mouse position.
Made a working example (open the console to see the result):
https://jsfiddle.net/dgw0sxu5/
<html>
<head>
<style>
body{
background: #000;
margin: 0
}
</style>
<script>
//Some object which is used to return an id from a click
//Added a fillStyle property for testing purposes
var Labels = [
{"id":"0","image":"1-0.png","name":"","xMax":"4956","xMin":"0","yMax":"50","yMin":"0","fillStyle":"pink"},
{"id":"1","image":"1-0.png","name":"","xMax":"4802","xMin":"4770","yMax":"156","yMin":"141","fillStyle":"red"},
{"id":"2","image":"1-0.png","name":"","xMax":"4895","xMin":"4810","yMax":"157","yMin":"141","fillStyle":"blue"},
{"id":"3","image":"1-0.png","name":"","xMax":"4923","xMin":"4903","yMax":"156","yMin":"145","fillStyle":"limegreen"},
{"id":"4","image":"1-0.png","name":"","xMax":"4956","xMin":"4931","yMax":"156","yMin":"145","fillStyle":"aqua"}
];
//Initialisiing for the testcase
window.onload = function(){
//The canvas used for click events
var tCanvas = document.body.appendChild(document.createElement('canvas'));
tCanvas.width = 4956; //Highest xMax value from labels
tCanvas.height = 157; //Highest yMax value from labels
//The graphical object
var tCTX = tCanvas.getContext('2d');
//Drawing the background
tCTX.fillStyle = '#fff';
tCTX.fillRect(0, 0, tCanvas.width, tCanvas.height);
//Drawing the rects for testing purposes
//The rectangles are kinda far on the right side
for(var i=0, j=Labels.length; i<j; i++){
tCTX.fillStyle = Labels[i].fillStyle;
tCTX.fillRect(+(Labels[i].xMin), +(Labels[i].yMin), +(Labels[i].xMax)-+(Labels[i].xMin), +(Labels[i].yMax)-+(Labels[i].yMin));
};
tCanvas.onclick = function(event){
var tX = event.clientX - this.offsetLeft + (document.body.scrollLeft || document.documentElement.scrollLeft), //X-Position of click in canvas
tY = event.clientY - this.offsetTop + (document.body.scrollTop || document.documentElement.scrollTop), //Y-Position of click in canvas
tR = []; //All found id at that position (can be more in theory)
//Finding the Labels fitting the click to their bounds
for(var i=0, j=Labels.length; i<j; i++){
if(tX >= +(Labels[i].xMin) && tX <= +(Labels[i].xMax) && tY >= +(Labels[i].yMin) && +(tY) <= +(Labels[i].yMax)){
tR.push(Labels[i].id)
}
};
console.log(
'Following ids found at the position #x. #y.: '
.replace('#x.', tX)
.replace('#y.', tY),
tR.join(', ')
)
}
}
</script>
</head>
<body></body>
</html>

First of all: what exactly did not work?
var canvas = document.getElementById("canvas"); should be outside of your function to save performance at a second call.
And getting coordinates is not complicated, but complex.
There is a great resource on how to get the right ones: http://javascript.info/coordinates
Be sure about the offset measured relative to the parent element (offsetParent and also your offsetLeft), document upper left (pageX) or viewport upper left (clientX).

Your logic seems to be correct, you just need to fix the syntax.
for (i = 0; i < labels.length; i++) {
if ((x>labels[i].xMin) && (x<labels[i].xMax) && (y>labels[i].yMin) && (y<labels[i].yMax)) {
console.log(labels[i].id)
}
}
Here is a complete example:
labels = [
{"id":"1","image":"1-0.png","name":"","xMax":"4802","xMin":"4770","yMax":"156","yMin":"141"},
{"id":"2","image":"1-0.png","name":"","xMax":"4895","xMin":"4810","yMax":"157","yMin":"141"},
{"id":"3","image":"1-0.png","name":"","xMax":"4923","xMin":"4903","yMax":"156","yMin":"145"},
{"id":"4","image":"1-0.png","name":"","xMax":"4956","xMin":"4931","yMax":"156","yMin":"145"}
]
var canvas = document.getElementById("canvas");
canvas.addEventListener("contextmenu", getPosition, false);
function getPosition(event) {
event.preventDefault();
var x = event.clientX;
var y = event.clientY;
var label = labels.find(function(label){
return (x>label.xMin) && (x<label.xMax) && (y>label.yMin) && (y<label.yMax)
});
if(label){
console.log("clicked label", label.id);
}else{
console.log("no label was clicked");
}
}

Related

How do I select an image from an array of images and draw the image on the canvas

I have encountered a problem with this project I'm working on. The code here is to draw an image anytime the mouse is clicked on the canvas. I have an array of country flags and I want to let the user select any flag of their choice and draw it on the canvas. When I click on the option to select any flag of my choice from the canvas, it only draws one particular flag. I want someone to help me find a way to select different flags from the array and draw them on the canvas.
for(var i = 0; i < 254; i++){
flags[i] = loadImage('assets/flags/a' + i + '.png');
}
this.fla = flags[i];
//where was the mouse on the last time draw was called.
//set it to -1 to begin with
var startMouseX = -1;
var startMouseY = -1;
this.draw = function(){
//if moused is pressed draw selected object at mouse locations on the canvas
if(mouseIsPressed){
//checks the size of the stamp object
var starSize = this.starSizeSlider.value();
var starX = mouseX - starSize/2;
var starY = mouseY - starSize/2;
for(var i = 0; i < flags.length; i++){
if(this.selectionToolForOptions.selected()== flags[i]){
image(flags[i], starX, starY, starSize, starSize);
}
else if (this.selectionToolForOptions.selected()=='cloud'){
image(this.flag, starX, starY, starSize, starSize);
}
}
}
}
//when the tool is deselected just show the drawing and and clear the options
this.unselectedTool = function() {
select("#sizeOfControl").html("");
}
//add options to select type of object to be selected and size the objects
this.populateOptions = function() {
this.textProperty = createDiv('Size: ');
this.textProperty.parent('#sizeOfControl');
this.starSizeSlider = createSlider(5,80,10);
this.starSizeSlider.parent("#sizeOfControl");
this.optionTextProperty = createDiv('Options: ');
this.optionTextProperty.parent("#sizeOfControl");
this.selectCountryFlag = createSelect('Star: ');
this.selectionToolForOptions = createSelect();
this.selectionToolForOptions.parent("#sizeOfControl");
for(var i = 0; i < flags.length; i++){
this.selectionToolForOptions.option(flags[i]);
}
}
The issue appears to be that you're specifying an object (a p5.Image instance) as the name of the options you are creating. Both the name and the value for select options must be strings. Because when p5.Image is converted to a string it is "[object Object]" all of your select options are the same.
Here's a simple working example.
let imgs;
function preload() {
imgs = {
"option A": loadImage("https://www.paulwheeler.us/files/existing-content.png"),
"option B": loadImage("https://www.paulwheeler.us/files/new-content.png")
};
}
let imgSelect;
function setup() {
createCanvas(400, 400);
imgSelect = createSelect();
for (const key in imgs) {
imgSelect.option(key);
}
imgSelect.position(10, 10);
background(220);
}
function draw() {
if (mouseIsPressed) {
image(imgs[imgSelect.value()], mouseX, mouseY);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

(JS) Splice to replace a value in an array only ever replacing the first value

I have a web page where I'm trying to select and replace/delete a value. When I click to select, in the console it shows that I'm selecting the item I want to select, but when the value is entered into the splice command it only changes the first entry of the array.
The first function is to select the bar based on the position and the section function is where the bar array value, a bar and button then appears and if the value is -1 it will delete the value and if not it will replace it
function clickBar(Event) {
//Receives position in xy for user clicks
var posX = Event.clientX;
var posY = Event.clientY;
console.log("X = "+posX +" Y = "+ posY);
var barWidth= 500/barVals.length;
console.log("Bar Width = "+barWidth);
var barNum;
//checks to see if click was within the confines of the area that the boxes are displayed
//if so barNum is calculated to show which position in the array you are clicking on
if(posY >topY && posY < bottomY && posX > leftX && posX < rightX){
console.log("Inside");
barNum = Math.floor((posX - leftX) / barWidth);
console.log("Bar Number = "+barNum);
} else {
console.log("Outside");}
if (barNum > -1) {
replaceBar(barNum)
}
console.log(barVals);
draw();
}
document.addEventListener("click", clickBar);
function replaceBar(barNum) {
console.log("Bar Number2 = "+barNum);
var replaceBox = document.getElementById('replaceVal');
var replaceBtn = document.getElementById('replaceBtn');
var displayBoxSetting = replaceBox.style.display;
var displayBtnSetting = replaceBtn.style.display;
//hiding and displaying the edit text box and button
if (displayBoxSetting == 'block' && displayBtnSetting=='block') {
replaceBox.style.display = 'none';
replaceBtn.style.display = 'none';
}
else {
replaceBox.style.display = 'block';
replaceBtn.style.display = 'block';
}
//Used to replace the value of the
if(replaceBox.value >0) {
barVals.splice(barNum, 1, parseInt(replaceBox.value));
colours.push(document.querySelector('input[name="colour"]:checked').value);
replaceBox.value = 0;
draw(); // redraw
}
else if(replaceBox.value == -1){
barVals.splice(barNum, 1);
replaceBox.value = 0;
draw(); // redraw
}
textBoxObj.value = 0;
replaceBox.style.display = 'none';
replaceBtn.style.display = 'none';
draw(); // redraw
}
The problem is:
if(replaceBox.value >0) {
barVals.splice(barNum, 1, parseInt(replaceBox.value));
It should be going from position barNum, of length 1, and taking the int value of the replace box and using it to splice the value
EDIT -
I've included an image of the page before the replace Screenshot of page and after 2nd Screenshot
Well it appears i had started about this in the completly wrong way so heres a different enough solution. This is put within the draw function, taking from the canvas itself.
canvas.onclick = function mousePos(Event) {
var rect = canvas.getBoundingClientRect();
var posX = Event.clientX- rect.left;
var posY = Event.clientY - rect.height;
console.log("X = "+posX +" Y = "+ posY);
var barWidth= 500/barVals.length;
console.log("Bar Width = "+barWidth);
var barNum;
barNum = Math.floor((posX-60) / barWidth);
if(barNum>=0 && barNum < barVals.length) {
var position = barNum;
console.log(position);
var amount = prompt("Please enter the new value", "734");
//barVals[position] = amount;
if(amount >0) {
barVals.splice(position, 1, parseInt(amount));
console.log(amount);
draw(); // redraw
}
else if(amount = -1){
barVals.splice(position, 1);
colours.splice(position, 1)
draw(); // redraw
}else{
alert("Incorrect value entered");
}
}
}

HTML Canvas & Javascript - Hover and Click Events

Below is a script which defines two functions that draw 4 rectangular buttons and 1 circular button respectively. I am trying to implement specific Hover and Click functionality into the buttons (as described in the script alerts) but I am at a bit of a loss as to how to do this. I tried calling the makeInteractiveButton() functions on each click but this caused a lot of odd overlap and lag. I want the script to do the following:
If the circular button is hovered, I would like it's fillColour to change and if it is clicked I would like it to change again to the colours described in the code (#FFC77E for hover, #FFDDB0 for clicked). This should only happen for the duration of the hover or click.
HTML:
<html lang="en">
<body>
<canvas id="game" width = "750" height = "500"></canvas>
<script type='text/javascript' src='stack.js'></script>
</body>
</html>
JavaScript:
var c=document.getElementById('game'),
canvasX=c.offsetLeft,
canvasY=c.offsetTop,
ctx=c.getContext('2d')
elements = [];
c.style.background = 'grey';
function makeInteractiveButton(x, strokeColor, fillColor) {
ctx.strokeStyle=strokeColor;
ctx.fillStyle=fillColor;
ctx.beginPath();
ctx.lineWidth=6;
ctx.arc(x, 475, 20, 0, 2*Math.PI);
ctx.closePath();
ctx.stroke();
ctx.fill();
elements.push({
arcX: x,
arcY: 475,
arcRadius: 20
});
}
b1 = makeInteractiveButton(235, '#FFFCF8', '#FFB85D');
c.addEventListener('mousemove', function(event) {
x=event.pageX-canvasX; // cursor location
y=event.pageY-canvasY;
elements.forEach(function(element) {
if (x > element.left && x < element.left + element.width &&
y > element.top && y < element.top + element.height) { // if cursor in rect
alert('Rectangle should undergo 5 degree rotation and 105% scale');
}
else if (Math.pow(x-element.arcX, 2) + Math.pow(y-element.arcY, 2) <
Math.pow(element.arcRadius, 2)) { // if cursor in circle
alert('Set b1 fillColour to #FFC77E.');
}
});
}, false);
c.addEventListener('click', function(event) {
x=event.pageX-canvasX; // cursor location
y=event.pageY-canvasY;
elements.forEach(function(element) {
if (x > element.left && x < element.left + element.width &&
y > element.top && y < element.top + element.height) { // if rect clicked
alert('Move all cards to centre simultaneously.');
}
else if (Math.pow(x-element.arcX, 2) + Math.pow(y-element.arcY, 2) <
Math.pow(element.arcRadius, 2)) { // if circle clicked
alert('Set b1 fillColour to #FFDDB0.');
}
});
}, false);
One way is keep all element data and write a hitTest(x,y) function but when you have a lot of complex shapes its better to use a secondary canvas to render element with their ID instead of their color in it and the color of x,y in second canvas is ID of hitted element, I should mention that the second canvas is'nt visible and its just a gelper for get the hitted element.
Github Sample:
https://siamandmaroufi.github.io/CanvasElement/
Simple implementation of hitTest for Rectangles :
var Rectangle = function(id,x,y,width,height,color){
this.id = id;
this.x=x;
this.y=y;
this.width = width;
this.height = height;
this.color = color || '#7cf';
this.selected = false;
}
Rectangle.prototype.draw = function(ctx){
ctx.fillStyle = this.color;
ctx.fillRect(this.x,this.y,this.width,this.height);
if(this.selected){
ctx.strokeStyle='red';
ctx.setLineDash([5,5]);
ctx.lineWidth = 5;
ctx.strokeRect(this.x,this.y,this.width,this.height);
}
}
Rectangle.prototype.hitTest=function(x,y){
return (x >= this.x) && (x <= (this.width+this.x)) &&
(y >= this.y) && (y <= (this.height+this.y));
}
var Paint = function(el) {
this.element = el;
this.shapes = [];
}
Paint.prototype.addShape = function(shape){
this.shapes.push(shape);
}
Paint.prototype.render = function(){
//clear the canvas
this.element.width = this.element.width;
var ctx = this.element.getContext('2d');
for(var i=0;i<this.shapes.length;i++){
this.shapes[i].draw(ctx);
}
}
Paint.prototype.setSelected = function(shape){
for(var i=0;i<this.shapes.length;i++){
this.shapes[i].selected = this.shapes[i]==shape;
}
this.render();
}
Paint.prototype.select = function(x,y){
for(var i=this.shapes.length-1;i>=0;i--){
if(this.shapes[i].hitTest(x,y)){
return this.shapes[i];
}
}
return null;
}
var el = document.getElementById('panel');
var paint = new Paint(el);
var rectA = new Rectangle('A',10,10,150,90,'yellow');
var rectB = new Rectangle('B',150,90,140,100,'green');
var rectC = new Rectangle('C',70,85,200,70,'rgba(0,0,0,.5)');
paint.addShape(rectA);
paint.addShape(rectB);
paint.addShape(rectC);
paint.render();
function panel_mouseUp(evt){
var p = document.getElementById('panel');
var x = evt.x - p.offsetLeft;
var y = evt.y - p.offsetTop;
var shape = paint.select(x,y);
if(shape){
alert(shape.id);
}
//console.log('selected shape :',shape);
}
function panel_mouseMove(evt){
var p = document.getElementById('panel');
var x = evt.x - p.offsetLeft;
var y = evt.y - p.offsetTop;
var shape = paint.select(x,y);
paint.setSelected(shape);
}
el.addEventListener('mouseup',panel_mouseUp);
el.addEventListener('mousemove',panel_mouseMove);
body {background:#e6e6e6;}
#panel {
border:solid thin #ccc;
background:#fff;
margin:0 auto;
display:block;
}
<canvas id="panel" width="400px" height="200px" >
</canvas>
just click or move over the shapes

Creating a click event in Javascript Canvas

I'm having trouble creating a click event for my Javascript canvas game. So far I have been following a tutorial, however the way you interact with the game is through mouse hover. I would like to change it so that instead of hovering over objects in the canvas to interact, I instead use a mouse click.
The following is the code I use to detect the mouse hover.
getDistanceBetweenEntity = function (entity1,entity2) //return distance
{
var vx = entity1.x - entity2.x;
var vy = entity1.y - entity2.y;
return Math.sqrt(vx*vx+vy*vy);
}
testCollisionEntity = function (entity1,entity2) //return if colliding
{
var distance = getDistanceBetweenEntity(entity1,entity2);
return distance < 50;
}
I then use this in a loop to interact with it.
var isColliding = testCollisionEntity(player,nounList[key]);
if(isColliding)
{
delete nounList[key];
player.score = player.score + 10;
}
Below is a complete copy of my game at its current state.
<canvas id="ctx" width="500" height="500" style="border:1px solid #000000;"></canvas>
<script>
var ctx = document.getElementById("ctx").getContext("2d");
ctx.font = '30px Arial';
//Setting the height of my canvas
var HEIGHT = 500;
var WIDTH = 500;
//Player class
var player =
{
x:50,
spdX:30,
y:40,
spdY:5,
name:'P',
score:0,
};
//Creating arrays
var nounList ={};
var adjectivesList ={};
var verbsList ={};
getDistanceBetweenEntity = function (entity1,entity2) //return distance
{
var vx = entity1.x - entity2.x;
var vy = entity1.y - entity2.y;
return Math.sqrt(vx*vx+vy*vy);
}
testCollisionEntity = function (entity1,entity2) //return if colliding
{
var distance = getDistanceBetweenEntity(entity1,entity2);
return distance < 50;
}
Nouns = function (id,x,y,name)
{
var noun =
{
x:x,
y:y,
name:name,
id:id,
};
nounList[id] = noun;
}
Adjectives = function (id,x,y,name)
{
var adjective =
{
x:x,
y:y,
name:name,
id:id,
};
adjectivesList[id] = adjective;
}
Verbs = function (id,x,y,name)
{
var verb =
{
x:x,
y:y,
name:name,
id:id,
};
verbsList[id] = verb;
}
document.onmousemove = function(mouse)
{
var mouseX = mouse.clientX;
var mouseY = mouse.clientY;
player.x = mouseX;
player.y = mouseY;
}
updateEntity = function (something)
{
updateEntityPosition(something);
drawEntity(something);
}
updateEntityPosition = function(something)
{
}
drawEntity = function(something)
{
ctx.fillText(something.name,something.x,something.y);
}
update = function ()
{
ctx.clearRect(0,0,WIDTH,HEIGHT);
drawEntity(player);
ctx.fillText("Score: " + player.score,0,30);
for(var key in nounList)
{
updateEntity(nounList[key]);
var isColliding = testCollisionEntity(player,nounList[key]);
if(isColliding)
{
delete nounList[key];
player.score = player.score + 10;
}
}
for(var key in adjectivesList)
{
updateEntity(adjectivesList[key])
var isColliding = testCollisionEntity(player,adjectivesList[key]);
if(isColliding)
{
delete adjectivesList[key];
player.score = player.score - 1;
}
}
for(var key in verbsList)
{
updateEntity(verbsList[key])
var isColliding = testCollisionEntity(player,verbsList[key]);
if(isColliding)
{
delete verbsList[key];
player.score = player.score - 1;
}
}
if(player.score >= 46)
{
ctx.clearRect(0,0,WIDTH,HEIGHT);
ctx.fillText("Congratulations! You win!",50,250);
ctx.fillText("Refresh the page to play again.",50,300);
}
}
Nouns('N1',150,350,'Tea');
Nouns('N2',400,450,'Park');
Nouns('N3',250,150,'Knee');
Nouns('N4',50,450,'Wall');
Nouns('N5',410,50,'Hand');
Adjectives('A1',50,100,'Broken');
Adjectives('A2',410,300,'Noisy');
Verbs('V1',50,250,'Smell');
Verbs('V2',410,200,'Walk');
setInterval(update,40);
To summarize all I want to do is change it so that instead of mousing over words to delete them you have to click.
(Apologies for not using correct terminology in places, my programming knowledge is quite limited.)
You can have your canvas listen for mouse clicks on itself like this:
// get a reference to the canvas element
var canvas=document.getElementById('ctx');
// tell canvas to listen for clicks and call "handleMouseClick"
canvas.onclick=handleMouseClick;
In the click handler, you'll need to know the position of your canvas relative to the viewport. That's because the browser always reports mouse coordinates relative to the viewport. You can get the canvas position relative to the viewport like this:
// get the bounding box of the canvas
var BB=canvas.getBoundingClientRect();
// get the left X position of the canvas relative to the viewport
var BBoffsetX=BB.left;
// get the top Y position of the canvas relative to the viewport
var BBoffsetY=BB.top;
So your mouseClickHandler might look like this:
// this function will be called when the user clicks
// the mouse in the canvas
function handleMouseClick(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// get the canvas postion relative to the viewport
var BB=canvas.getBoundingClientRect();
var BBoffsetX=BB.left;
var BBoffsetY=BB.top;
// calculate the mouse position
var mouseX=e.clientX-BBoffsetX;
var mouseY=e.clientY-BBoffsetY;
// report the mouse position using the h4
$position.innerHTML='Click at '+parseInt(mouseX)+' / '+parseInt(mouseY);
}
If your game doesn't let the window scroll or resize then the canvas postion won't change relative to the viewport. Then, for better performance, you can move the 3 lines relating to getting the canvas position relative to the viewport to the top of your app.
// If the browser window won't be scrolled or resized then
// get the canvas postion relative to the viewport
// once at the top of your app
var BB=canvas.getBoundingClientRect();
var BBoffsetX=BB.left;
var BBoffsetY=BB.top;
function handleMouseClick(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// calculate the mouse position
var mouseX=e.clientX-BBoffsetX;
var mouseY=e.clientY-BBoffsetY;
// report the mouse position using the h4
$position.innerHTML='Click at '+parseInt(mouseX)+' / '+parseInt(mouseY);
}

Draw clickable grid of 1 million squares

I need to find a way to draw a 1000x1000 squares grid, each square is clickable and they must be independently color changeable. Like mines game. I can use HTML (pure or using Canvas or SVG), CSS and JavaScript for this.
I know how to create one grid with these characteristics with JavaScript and CSS, it does well with 10x10 squares, with 100x100 the squares will turn into tall rectangles and 1000x1000 it loads, but the "squares" are soo much compressed that borders meet each other and renders a full gray page.
I tried using HTML and JavaScript to draw SVG squares, the squares' size problem solves, but I don't know how to make they change color when clicked and when I set to load 1000x1000 squares it will freeze the browse and eventually crash the tab.
Is this feasible in any way?
EDIT
Sorry if I wasn't clear, but yes, I need scroll bars in that. They are no problem for me.
You can see the two trials I described here:
JavaScript and CSS
var lastClicked;
var grid = clickableGrid(100,100,function(el,row,col,i){
console.log("You clicked on element:",el);
console.log("You clicked on row:",row);
console.log("You clicked on col:",col);
console.log("You clicked on item #:",i);
el.className='clicked';
if (lastClicked) lastClicked.className='';
lastClicked = el;
});
document.body.appendChild(grid);
function clickableGrid( rows, cols, callback ){
var i=0;
var grid = document.createElement('table');
grid.className = 'grid';
for (var r=0;r<rows;++r){
var tr = grid.appendChild(document.createElement('tr'));
for (var c=0;c<cols;++c){
var cell = tr.appendChild(document.createElement('td'));
++i;
cell.addEventListener('click',(function(el,r,c,i){
return function(){
callback(el,r,c,i);
}
})(cell,r,c,i),false);
}
}
return grid;
}
.grid { margin:1em auto; border-collapse:collapse }
.grid td {
cursor:pointer;
width:30px; height:30px;
border:1px solid #ccc;
}
.grid td.clicked {
background-color:gray;
}
JavaScript and HTML
document.createSvg = function(tagName) {
var svgNS = "http://www.w3.org/2000/svg";
return this.createElementNS(svgNS, tagName);
};
var numberPerSide = 20;
var size = 10;
var pixelsPerSide = 400;
var grid = function(numberPerSide, size, pixelsPerSide, colors) {
var svg = document.createSvg("svg");
svg.setAttribute("width", pixelsPerSide);
svg.setAttribute("height", pixelsPerSide);
svg.setAttribute("viewBox", [0, 0, numberPerSide * size, numberPerSide * size].join(" "));
for(var i = 0; i < numberPerSide; i++) {
for(var j = 0; j < numberPerSide; j++) {
var color1 = colors[(i+j) % colors.length];
var color2 = colors[(i+j+1) % colors.length];
var g = document.createSvg("g");
g.setAttribute("transform", ["translate(", i*size, ",", j*size, ")"].join(""));
var number = numberPerSide * i + j;
var box = document.createSvg("rect");
box.setAttribute("width", size);
box.setAttribute("height", size);
box.setAttribute("fill", color1);
box.setAttribute("id", "b" + number);
g.appendChild(box);
svg.appendChild(g);
}
}
svg.addEventListener(
"click",
function(e){
var id = e.target.id;
if(id)
alert(id.substring(1));
},
false);
return svg;
};
var container = document.getElementById("container");
container.appendChild(grid(100, 10, 2000, ["gray", "white"]));
<div id="container">
</div>
I will be trying implementing the given answers and ASAP I'll accept or update this question. Thanks.
SOLUTION
Just to record, I managed to do it using canvas to draw the grid and the clicked squares and added an event listener to know where the user clicks.
Here is the code in JavaScript and HTML:
function getSquare(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: 1 + (evt.clientX - rect.left) - (evt.clientX - rect.left)%10,
y: 1 + (evt.clientY - rect.top) - (evt.clientY - rect.top)%10
};
}
function drawGrid(context) {
for (var x = 0.5; x < 10001; x += 10) {
context.moveTo(x, 0);
context.lineTo(x, 10000);
}
for (var y = 0.5; y < 10001; y += 10) {
context.moveTo(0, y);
context.lineTo(10000, y);
}
context.strokeStyle = "#ddd";
context.stroke();
}
function fillSquare(context, x, y){
context.fillStyle = "gray"
context.fillRect(x,y,9,9);
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
drawGrid(context);
canvas.addEventListener('click', function(evt) {
var mousePos = getSquare(canvas, evt);
fillSquare(context, mousePos.x, mousePos.y)
}, false);
<body>
<canvas id="myCanvas" width="10000" height="10000"></canvas>
</body>
Generating such a large grid with HTML is bound to be problematic.
Drawing the grid on a Canvas and using a mouse-picker technique to determine which cell was clicked would be much more efficient.
This would require 1 onclick and/or hover event instead of 1,000,000.
It also requires much less HTML code.
I wouldn't initialize all the squares right off, but instead as they are clicked -
(function() {
var divMain = document.getElementById('main'),
divMainPosition = divMain.getBoundingClientRect(),
squareSize = 4,
square = function(coord) {
var x = coord.clientX - divMainPosition.x + document.body.scrollLeft +
document.documentElement.scrollLeft,
y = coord.clientY - divMainPosition.y + document.body.scrollTop +
document.documentElement.scrollTop;
return {
x:Math.floor(x / squareSize),
y:Math.floor(y / squareSize)
}
}
divMain.addEventListener('click', function(evt) {
var sqr = document.createElement('div'),
coord = square(evt);
sqr.className = 'clickedSquare';
sqr.style.width = squareSize + 'px';
sqr.style.height = squareSize + 'px';
sqr.style.left = (coord.x * squareSize) + 'px';
sqr.style.top = (coord.y * squareSize) + 'px';
sqr.addEventListener('click', function(evt) {
console.log(this);
this.parentNode.removeChild(this);
evt.stopPropagation();
});
this.appendChild(sqr);
});
}());
#main {
width:4000px;
height:4000px;
background-color:#eeeeee;
position:relative;
}
.clickedSquare {
background-color:#dd8888;
position:absolute;
}
<div id="main">
</div>
Uses CSS positioning to determine which square was clicked on,
doesn't initialize a square until it's needed.
Granted I imagine this would start to have a negative impact to use r experience, but that would ultimately depend on their browser and machine.
Use the same format you noramlly use, but add this:
sqauareElement.height = 10 //height to use
squareElement.width = 10 //width to use
This will add quite a large scroll due to the size, but it's the only logical explanation I can come up with.
The canvas approach is fine, but event delegation makes it possible to do this with a table or <div> elements with a single listener:
const tbodyEl = document.querySelector("table tbody");
tbodyEl.addEventListener("click", event => {
const cell = event.target.closest("td");
if (!cell || !tbodyEl.contains(cell)) {
return;
}
const row = +cell.getAttribute("data-row");
const col = +cell.getAttribute("data-col");
console.log(row, col);
});
const rows = 100;
const cols = 100;
for (let i = 0; i < rows; i++) {
const rowEl = document.createElement("tr");
tbodyEl.appendChild(rowEl);
for (let j = 0; j < cols; j++) {
const cellEl = document.createElement("td");
rowEl.appendChild(cellEl);
cellEl.classList.add("cell");
cellEl.dataset.row = i;
cellEl.dataset.col = j;
}
}
.cell {
height: 4px;
width: 4px;
cursor: pointer;
border: 1px solid black;
}
table {
border-collapse: collapse;
}
<table><tbody></tbody></table>

Categories