I have a set of buttons that each add an image on the next click at the position of the click inside a specific div. However, I'm facing a problem that after I've clicked a button every click will add the image until I hit a different button. How can I make it so clicking the button only allows the onmousedown function to be called once?
Here is what I have:
function makeSnow() {
document.body.style.backgroundColor = "red";
document.getElementById("canvas").onmousedown = function() {
var x = event.clientX;
var y = event.clientY;
var pic = document.getElementById("snowballAppear");
pic.style.display = '';
pic.style.position = 'absolute';
pic.style.left = x - 50 + 'px';
pic.style.top = y - 50 + 'px';
document.body.style.backgroundColor = 'blue';
};
};
function makeCat() {
document.body.style.backgroundColor = "red";
document.getElementById("canvas").onmousedown = function() {
var x = event.clientX;
var y = event.clientY;
var pic = document.getElementById("catAppear");
pic.style.display = '';
pic.style.position = 'absolute';
pic.style.left = x - 50 + 'px';
pic.style.top = y - 50 + 'px';
document.body.style.backgroundColor = 'blue';
};
};
function makeDog() {
document.body.style.backgroundColor = "red";
document.getElementById("canvas").onmousedown = function() {
var x = event.clientX;
var y = event.clientY;
var pic = document.getElementById("dogAppear");
pic.style.display = '';
pic.style.position = 'absolute';
pic.style.left = x - 50 + 'px';
pic.style.top = y - 50 + 'px';
document.body.style.backgroundColor = 'blue';
};
};
<div id="container" class "container">
<div id='canvas' style="background-color: green; width: 300px; height: 300px;">
</div>
<ul>
<button onClick="makeSnow()">
<li>snow</li>
</button>
<button onClick="makeCat()">
<li>cat</li>
</button>
<button onClick="makeDog()">
<li>dog</li>
</button>
</ul>
<div class "picture">
<img alt="snowballAppear" id="snowballAppear" style="display: none; width: 100px; height: 100px;" src="http://vignette3.wikia.nocookie.net/pottermore/images/9/99/Snowball-lrg.png/revision/latest?cb=20130412122815" />
</div>
<div class "picture">
<img alt="catAppear" id="catAppear" style="display: none; width: 100px; height: 100px;" src="https://images.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg?h=350&auto=compress&cs=tinysrgb" />
</div>
<div class "picture">
<img alt="dogAppear" id="dogAppear" style="display: none; width: 100px; height: 100px;" src="https://images.pexels.com/photos/39317/chihuahua-dog-puppy-cute-39317.jpeg?h=350&auto=compress&cs=tinysrgb" />
</div>
</div>
You need to replace the onmousedown event listener with a function that does nothing, in the original onmousedown event listener. Like this:
function makeCat() {
document.body.style.backgroundColor = "red";
document.getElementById("canvas").onmousedown = function() {
...
document.getElementById("canvas").onmousedown = () => {};
};
};
There are three ways to bind an event to an element in JavaScript, the old fashionned spaghetti style that is much hated these days, the intermediate style with property affectation that is not so bad but not really a good practice, and the trendy style that is using addEventListener.
Old fashionned spaghetti style :
function sayHello() {
console.log("hello");
}
<button onclick="sayHello()">Say hello</button>
Intermediate style with property affectation :
var btn = document.getElementById("btn");
btn.onclick = sayHello;
function sayHello() {
console.log("hello");
}
<button id="btn">Say hello</button>
Trendy style with addEventListener :
var btn = document.getElementById("btn");
btn.addEventListener("click", sayHello);
function sayHello() {
console.log("hello");
}
<button id="btn">Say hello</button>
The last two styles avoid the HTML+JS soup. This is considered a good practice that helps to produce "unobstrusive JavaScript" (Wikipedia to the rescue). That being said, the third approach is often the best option in that it makes it easy to deal with multiple listeners :
var btn = document.getElementById("btn");
btn.addEventListener("click", sayHello);
btn.addEventListener("click", sayGoodbye);
function sayHello() {
console.log("hello");
}
function sayGoodbye() {
console.log("goodbye");
}
<button id="btn">Say hello and goodbye</button>
Now to solve the "one click, one picture" problem you need to stop listening whenever the user clicks on the button. To do this you can either reset the onclick property, or use a new function called removeEventListener. One snippet for both options :
var btn = document.getElementById("btn");
btn.onclick = sayHello;
btn.addEventListener("click", sayGoodbye);
function sayHello() {
console.log("hello");
btn.onclick = null;
}
function sayGoodbye() {
console.log("goodbye");
btn.removeEventListener("click", sayGoodbye);
}
<button id="btn">Say hello and goodbye once</button>
Another possibility would be to use a global variable combined with an if statement. Not a best practice, but I believe it's worth mentionning it :
var shouldSayHello = false;
var masterBtn = document.getElementById("master-btn");
var slaveBtn = document.getElementById("slave-btn");
masterBtn.onclick = unlock;
slaveBtn.onclick = sayHello;
slaveBtn.style.color = "gray";
function unlock() {
shouldSayHello = true;
slaveBtn.style.color = "black";
}
function sayHello() {
if (shouldSayHello) {
console.log("hello");
shouldSayHello = false;
slaveBtn.style.color = "gray";
}
}
<button id="master-btn">Unlock</button>
<button id="slave-btn">Say hello once</button>
All this to finally answer the actual question. Note that you have to clone the hidden image element with the cloneNode function in order to add multiple cats to the canvas (we only need cats for the demo) :
var catBtn = document.getElementById("cat-btn");
catBtn.addEventListener("click", prepareCatLanding);
function prepareCatLanding() {
var canvas = document.getElementById("canvas");
document.body.style.backgroundColor = "red";
canvas.addEventListener("mousedown", appendCat);
};
function appendCat() {
var x = event.clientX;
var y = event.clientY;
var src = document.getElementById("catAppear");
this.removeEventListener("mousedown", appendCat);
pic = src.cloneNode();
pic.id = '';
pic.style.display = '';
pic.style.position = 'absolute';
pic.style.left = x - 50 + 'px';
pic.style.top = y - 50 + 'px';
src.parentNode.appendChild(pic);
document.body.style.backgroundColor = 'blue';
}
<div id="container" class="container">
<ul>
<li><button id="cat-btn">cat</button></li>
</ul>
<div id="canvas" style="background-color: green; width: 300px; height: 300px;">
</div>
<div class="picture">
<img alt="catAppear" id="catAppear" style="display: none; width: 100px; height: 100px;" src="https://images.pexels.com/photos/104827/cat-pet-animal-domestic-104827.jpeg?h=350&auto=compress&cs=tinysrgb" />
</div>
</div>
I've fixed the HTML along the way. Indeed, <button><li></li></button> is not valid since <ul> only accepts list items, it should be the opposite, that is <li><button></button></li>. Moreover, class "container" should be class="container".
At the end of your event listener, you could reset the mousedown to something else that doesn't do anything:
function makeSnow() {
document.body.style.backgroundColor = "red";
document.getElementById("canvas").onmousedown = function() {
var x = event.clientX;
var y = event.clientY;
var pic = document.getElementById("catAppear");
pic.style.display = '';
pic.style.position = 'absolute';
pic.style.left = x - 50 + 'px';
pic.style.top = y - 50 + 'px';
document.body.style.backgroundColor = 'blue';
document.getElementById("canvas").onmousedown = function() {
//do nothing;
};
};
};
And then the same for the other functions.
You could use the return; statement in your functions to exit them.
You can return right after image is added, add an condition to your liking, I am not sure what exacly is your problem.
if (some condition) return;
But before anything:
class "container" and many more like it is not how you write classes in HTML, the correct notation is class="name"
The accepted answer suggests to redefine onmousedown event handler, which would work but requires you to write superfluous code. Here is shorter version by the way =).
document.getElementById("canvas").onmousedown = null;
It seems like a simple event listener which triggers only once is a suitable solution as well.
document.getElementById("canvas").addEventListener("mousedown", functionToExecuteOnceEventOccurs, { once: true });
Take a look at docs on MDN. Pay attention to browser compatibility table though, "once" option is not that widely supported.
Try to use:
break; or return; at the end of each function
Related
I try to move the image in every click but it just move one time.See the code:
<img id="myImg" src="">
<script>
function move() {
var img = document.getElementById("myImg")
img.style.left += "2px";
}
</script>
<button onclick="move()"></button>
Anyone knows how to make it work?
When you get the value of left property, it looks like this: 0px. It's a string. So if you want to calculate it, you need to parse it to a number. Before doing that, you can split px part to get the number.
img {
position: absolute;
width: 150px;
height: 100px;
left: 0px;
}
button {
margin-top: 100px;
}
<img id = "myImg" src = "https://images.unsplash.com/photo-1474511320723-9a56873867b5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80">
<script>
function move(){
var img = document.getElementById("myImg");
img.style.left = +img.style.left.substring(0, img.style.left.length - 2) + 2 + 'px';
}
</script>
<button onclick = "move()">Move</button>
This line
img.style.left += "2px";
produces invalid CSS. You want to extract the integer each time, add 2, then add the "px" unit back in.
const img = document.getElementById("myImg")
function move() {
img.style.marginLeft = img.style.marginLeft ? parseInt(img.style.marginLeft, 10) + 2 + "px" : "2px"
}
<img id="myImg" src="https://placekitten.com/200/200">
<button onclick="move()">Move Image</button>
This is a very clumsy way to move an image. Please educate yourself about canvas object. But as you are a begginer and may just need a quick fix, here's a way to get your code working:
<img id = "myImg" style="width: 100px; height: 100px;" src ="https://upload.wikimedia.org/wikipedia/en/thumb/e/ec/Soccer_ball.svg/600px-Soccer_ball.svg.png">
<script>
var margin = 0;
function move(){
var img = document.getElementById("myImg")
margin += 2;
img.style.marginLeft = margin + "px";
}
</script>
<button onclick = "move()" >Move</button>
if you want to move image then you could move it either right or left for that try to fix your code at left - px
<img id = "myImg" src = "">
<script>
function move(){
var img = document.getElementById("myImg")
img.style.left += "2px";
}
function moveleft() {
imgObj.style.left = parseInt(imgObj.style.left) + 10 + 'px';
}
</script>
<button onclick = "moveleft();">
I would like to know the best possible way to style a width of a box with button click. I want to create a code where every time i click a button, the width of the box (in this case) increases.
var btn = document.querySelector(".btn");
var box = document.getElementById('bebo');
var range = ['100px','200px','300px','400px','500px','600px'];
btn.addEventListener('click', function(){
box.style.width = range[0],
box.style.width = range[1],
box.style.width = range[2],
box.style.width = range[3],
box.style.width = range[4],
box.style.width = range[5];
});
This is what i tried but it doesn't work. I've also tried this:
var range = ['100px','200px','300px','400px','500px','600px'];
btn.addEventListener('click', function(){
box.style.width = range.shift();
});
The range.shift() option did work for me but i don't feel like that's the efficient way to code for what i want.
I would really like some advice.
Your code actually works fine, but since it mutates the array, you cannot reuse it. In addition, when the array is empty, more clicks would cause the width to be undefined.
I would use a counter. You can increment the counter, and when you reach the end of the array, you can go back to the start (like in this example).
Example:
var btn = document.querySelector(".btn");
var box = document.getElementById('bebo');
var range = ['100px', '200px', '300px', '400px', '500px', '600px'];
var counter = 0;
btn.addEventListener('click', function() {
box.style.width = range[counter];
counter = (counter + 1) % range.length;
});
#bebo {
background: red;
width: 10px;
height: 100px;
}
<button class="btn">Btn</button>
<div id="bebo"></div>
I'm learning JavaScript and how to manipulate DOM by using JavaScript eventListeners and not use any jQuery. I have managed to come this far and couldn't think straight on how to apply the random color and erase functions to work. I know I screwed up somewhere. When I click random color i should be able to fill with random rgba and if I click erase I should be able to erase only the boxes i selected, it should revert back to grey color.
I really appreciate your feedback on how i can fix this issue. Thank you a bunch.
const container = document.getElementById("container");
const resetButton = document.querySelector("#reset");
const eraseButton = document.querySelector("#erase");
const randomColor = document.querySelector("#color");
let boxes = 16;
createGrid(boxes);
function createGrid(boxes){
for(i=0; i<boxes*boxes; i++){
const div = document.createElement('div');
div.setAttribute=('id', 'box');
div.style.width = 450/boxes + 'px';
div.style.height = 450/boxes + 'px';
div.style.margin = '0px';
div.style.padding = '0px';
div.style.display = 'inline-block';
div.style.border = '2px solid black';
div.style.backgroundColor = 'grey';
container.appendChild(div);
//event listeners
div.addEventListener("mouseover", function(event){
this.style.backgroundColor = 'orange';
// console.log(this);
this.style.boxShadow = '0 0 8px rgba(0,0,0,0.6)';
this.style.borderColor = 'blue';
});
div.addEventListener("mouseover", function(event){
this.style.backgroundColor = color;
console.log(this);
})
}
}
// reset the grid to original status
function resetGrid(){
while(container.firstChild){
container.removeChild(container.firstChild);
}
}
//event listeners to reset the grid
resetButton.addEventListener("click", () =>{
resetGrid();
createGrid(boxes);
});
// event listeners to clear 1 or more grids not working
eraseButton.addEventListener("click", () => {
this.style.backgroundColor = "grey";
});
//event listeners to pick random color
randomColor.addEventListener("click", () =>{
let color = 'rgb(' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ')';
this.style.backgroundColor = color;
console.log(this);
});
<h1>Etch-A-Sketch</h1>
<div id="container"></div>
<button class="btn btn-primary" onclick="resetGrid()" id="reset">RESET</button>
<button class="btn btn-primary" onclick="eraseGrid()" id="erase">ERASE</button>
<button class="btn btn-primary" onclick="randomColor()" id="color">RANDOM COLOR </button>
You added function on onclick attribute also binded them with EventListener.
Keep Current function in variable which helps to remove it from EventListener.
Create 3 Separate function's for Coloring the Div's. ( regular, erase & random )
Remove previous function and Add new function to EventListener while OnClicking Button's.
// constant variables
const container = document.getElementById("container");
const resetButton = document.querySelector("#reset");
const eraseButton = document.querySelector("#erase");
const randomColor = document.querySelector("#color");
// functions to Color Div's
var regular_coloring = function()
{
(this).style.backgroundColor = 'orange';
(this).style.boxShadow = '0 0 8px rgba(0,0,0,0.6)';
(this).style.borderColor = 'blue';
}
var erase_coloring = function()
{
(this).style.backgroundColor = 'grey';
(this).style.boxShadow = '0 0 0 rgba(0,0,0,0)';
(this).style.borderColor = 'black';
}
var random_coloring = function()
{
(this).style.backgroundColor = 'rgb(' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ',' + (Math.floor(Math.random() * 256)) + ')';
(this).style.boxShadow = '0 0 8px rgba(0,0,0,0.6)';
(this).style.borderColor = 'blue';
}
// set current function and create Grid
let currentFunction = regular_coloring;
let boxes = 16;
createGrid(boxes);
function createGrid(boxes)
{
currentFunction = regular_coloring;
for(i=0; i<boxes*boxes; i++)
{
const div = document.createElement('div');
div.setAttribute('data-type', 'box');
div.style.width = 450/boxes + 'px';
div.style.height = 450/boxes + 'px';
div.style.margin = '0px';
div.style.padding = '0px';
div.style.display = 'inline-block';
div.style.border = '2px solid black';
div.style.backgroundColor = 'grey';
container.appendChild(div);
//event listeners
div.addEventListener("mouseover", currentFunction);
if((i + 1) % 16 === 0 )
{
br_tag = document.createElement('br');
container.appendChild(br_tag);
}
}
}
// reset the grid to original status
function resetGrid(){
while(container.firstChild){
container.removeChild(container.firstChild);
}
}
// event listeners to reset the grid
resetButton.addEventListener("click", () =>{
resetGrid();
createGrid(boxes);
});
// event listeners to clear grid
eraseButton.addEventListener("click", () => {
myDivs = document.querySelectorAll('[data-type=box]');
for (var i = 0; i < myDivs.length; i++)
{
myDivs[i].removeEventListener('mouseover', currentFunction);
myDivs[i].addEventListener('mouseover', erase_coloring);
}
currentFunction = erase_coloring;
});
//event listeners for random color
randomColor.addEventListener("click", () => {
myDivs = document.querySelectorAll('[data-type=box]');
for (var i = 0; i < myDivs.length; i++)
{
myDivs[i].removeEventListener('mouseover', currentFunction);
myDivs[i].addEventListener('mouseover', random_coloring);
}
currentFunction = random_coloring;
});
<div><span>Etch-A-Sketch</span>
<button class="btn btn-primary" id="reset">Reset</button>
<button class="btn btn-primary" id="erase">Erase</button>
<button class="btn btn-primary" id="color">Random</button></div><br>
<div id="container"></div>
I have a simple game where image moves randomly and score increases when user clicks on it.
The first image displays before game is started, which when clicked calls the play() function in javascript, which hides that image and displays the image that is to be used for the game.
That is where my code is stuck, it does not call the function play(). I am new to javascript and html. Any help would be great!
Here is my code
<html>
<head>
<title>Image click Game!</title>
<script>
global var score = 0;
global var left=400;
global var top = 100;
function play() {
var game = document.getElementById('game');
var firstDiv = document.getElementById('firstDiv');
var height = window.innerHeight;
var width = window.innerWidth;
firstDiv.style = 'display : none';
game.style='display : block';
setInterval("move()", 1000);
}
function move() {
var randomNumberX = Math.floor(Math.random()*11)-5;
var randomNumberY = Math.floor(Math.random()*11)-5;
left = left + randomNumberX;
top = top+randomNumberY;
var im = document.getElementById('image');
im.style.left = left;
im.style.top = top;
}
</script>
</head>
<body>
<div id ="firstDiv" style="display : block">
<img src="pics/playgame.gif" width="108" height="106" onClick = "play()"/></a>
</div>
<div id="game" style="display : none">
<p>"Score: " + score</p>
<img id="image" src="pics/gameImage.gif" onClick = "score++" style="position:absolute; left: 400; top: 100;"/></a>
</div>
</body>
</html>
There are a handful of things wrong with your code:
1) Your <img> tags are ended with a stray, unneeded </a> tag.
2) In your <img> tag, you should change to onClick = "play();"
3) I don't believe javascript supports the global keyword in that way.
4) To change CSS style, try this:
firstDiv.style.display = 'none';
game.style.display = 'block';
5) You cannot display javascript variables in this fashion: <p>"Score: " + score</p>...not to mention there is no declared variable 'score' to begin with!
Keep working at it, you only get better with practice.
Tyr this
<script>
var score = 0;
var left=400;
var top = 100;
function play() {
var game = document.getElementById('game');
var firstDiv = document.getElementById('firstDiv');
var height = window.innerHeight;
var width = window.innerWidth;
firstDiv.style.display='none';
game.style.display='block';
setInterval("move()", 1000);
}
function move() {
var randomNumberX=Math.floor(Math.random()*11)-5;
var randomNumberY=Math.floor(Math.random()*11)-5;
left= left+randomNumberX;
top = top+randomNumberY;
var im= document.getElementById('image');
im.style.left=left;
im.style.top=top;
}
I want to use the 'mouse's drag' to drag a background's position around, inside a box.
The CSS:
.filmmakers #map {
width : 920px;
height : 500px;
margin-top : 50px;
margin-left : 38px;
border : 1px solid rgb(0, 0, 0);
cursor : move;
overflow : hidden;
background-image : url('WorldMap.png');
background-repeat : no-repeat;
}
The html:
<div id = "map" src = "WorldMap.png" onmousedown = "MouseMove(this)"> </div>
The Javascript:
function MouseMove (e) {
var x = e.clientX;
var y = e.clientY;
e.style.backgroundPositionX = x + 'px';
e.style.backgroundPositionY = y + 'px';
e.style.cursor = "move";
}
Nothing happens, no errors, no warnings, nothing... I have tried lots of things: an absolutely positioned image inside a div (you can guess why that didn't work), A draggable div inside a div with a background image, a table with drag and drop, and finally I tried this:
function MouseMove () {
e.style.backgroundPositionX = 10 + 'px';
e.style.backgroundPositionY = 10 + 'px';
e.style.cursor = "move";
}
This works, but its not relative to the mouse's position, pageX and pageY don't work either.
A live demo: http://jsfiddle.net/VRvUB/224/
P.S: whatever your idea is, please don't write it in JQuery
From your question I understood you needed help implementing the actual "dragging" behavior. I guess not. Anyway, here's the results of my efforts: http://jsfiddle.net/joplomacedo/VRvUB/236/
The drag only happens when the mouse button, and.. well, it behaves as I think you might want it to. Just see the fiddle if you haven't =)
Here's the code for those who want to see it here:
var AttachDragTo = (function () {
var _AttachDragTo = function (el) {
this.el = el;
this.mouse_is_down = false;
this.init();
};
_AttachDragTo.prototype = {
onMousemove: function (e) {
if ( !this.mouse_is_down ) return;
var tg = e.target,
x = e.clientX,
y = e.clientY;
tg.style.backgroundPositionX = x - this.origin_x + this.origin_bg_pos_x + 'px';
tg.style.backgroundPositionY = y - this.origin_y + this.origin_bg_pos_y + 'px';
},
onMousedown: function(e) {
this.mouse_is_down = true;
this.origin_x = e.clientX;
this.origin_y = e.clientY;
},
onMouseup: function(e) {
var tg = e.target,
styles = getComputedStyle(tg);
this.mouse_is_down = false;
this.origin_bg_pos_x = parseInt(styles.getPropertyValue('background-position-x'), 10);
this.origin_bg_pos_y = parseInt(styles.getPropertyValue('background-position-y'), 10);
},
init: function () {
var styles = getComputedStyle(this.el);
this.origin_bg_pos_x = parseInt(styles.getPropertyValue('background-position-x'), 10);
this.origin_bg_pos_y = parseInt(styles.getPropertyValue('background-position-y'), 10);
//attach events
this.el.addEventListener('mousedown', this.onMousedown.bind(this), false);
this.el.addEventListener('mouseup', this.onMouseup.bind(this), false);
this.el.addEventListener('mousemove', this.onMousemove.bind(this), false);
}
};
return function ( el ) {
new _AttachDragTo(el);
};
})();
/*** IMPLEMENTATION ***/
//1. Get your element.
var map = document.getElementById('map');
//2. Attach the drag.
AttachDragTo(map);
This isn't working because you are passing the element "map" to your MouseMove function, and using it as both an event object and an element. You can fix this painlessly by using JavaScript to assign your event handler rather than HTML attributes:
<div id="map"></div>
And in your JavaScript:
document.getElementById('map').onmousemove = function (e) {
// the first parameter (e) is automatically assigned an event object
var x = e.clientX;
var y = e.clientY;
// The context of this is the "map" element
this.style.backgroundPositionX = x + 'px';
this.style.backgroundPositionY = y + 'px';
}
http://jsfiddle.net/VRvUB/229/
The downside of this approach is that the backgroundPositionX and backgroundPositionY style properties are not supported in all browsers.
You mention "an absolutely positioned image inside a div" which is probably the more compatible solution for this. To make this setup work, you need to set the position of the outer element to relative, which makes absolute child elements use its bounds as zero.
<div id="map">
<img src="" alt="">
</div>
CSS:
#map {
position:relative;
overflow:hidden;
}
#map img {
position:absolute;
left:0;
top:0;
}
Here it is applied to your code: http://jsfiddle.net/VRvUB/232/
This works 100%
Vanilla Javascript
document.getElementById('image').onmousemove = function (e) {
var x = e.clientX;
var y = e.clientY;
this.style.backgroundPositionX = -x + 'px';
this.style.backgroundPositionY = -y + 'px';
this.style.backgroundImage = 'url(https://i.ibb.co/vhL5kH2/image-14.png)';
}
document.getElementById('image').onmouseleave = function (e) {
this.style.backgroundPositionX = 0 + 'px';
this.style.backgroundPositionY = 0 + 'px';
this.style.backgroundImage = 'url(https://i.ibb.co/Ph9MCB2/template.png)';
}
.container {
max-width: 670px;
height: 377px;
}
#image {
max-width: 670px;
height: 377px;
cursor: crosshair;
overflow: hidden;
background-image: url('https://i.ibb.co/Ph9MCB2/template.png');
background-repeat: no-repeat;
}
<div class="container">
<div id="image">
</div>
</div>