Why aren't mouse events working - javascript

This is a big piece of code, but if you will indulge me:
<html>
<head>
<style>
.mobile {position: absolute;}
</style>
<script>
var images = new Array();
image['Key1']='image1.png';
image['Key2']='image2.png';
image['Key3']='image3.png';
image['Key4']='image4.png';
image['Key5']='image5.png';
function createAll()
{
tag = document.getElementById("canvas");
for(var key in image)
{
var r = key;
while(r.indexOf('_')>-1)
{
r=r.replace('_',' ');
}
let t = document.createElement("p");
t.id=r;
t.className="mobile"
t.xVel = Math.floor(Math.random()*50-25);
t.yVel = Math.floor(Math.random()*50-25);
t.xPos = Math.floor(Math.random()*1000)-60;
t.style.left= t.xPos;
t.onclick="clickTag('"+r+"')";
t.yPos=Math.floor(Math.random()*600)-42;
////THIS IS WHERE THE EVENT IS ADDED////
t.addEventListener("onmousedown", function(){clickTag(t);});
////THIS IS WHERE THE EVENT IS ADDED////
t.style.top=t.yPos;
var i = document.createElement("img");
i.src=image[key];
var s = document.createElement("span");
tag.appendChild(t);
t.appendChild(i);
t.appendChild(s);
setTimeout(function() {step(t);},200);
}
}
function restartMe(tag)
{
var x = Math.floor(Math.random()*1000);
var y = Math.floor(Math.random()*600);
var xVel = Math.floor(Math.random()*50-25);
var yVel = Math.floor(Math.random()*50-25);
var r = Math.random();
if(r<.25)//left wall
{
x=-59;
xVel = Math.floor(Math.random()*10);
}
else if(r<.5)//right wall
{
x=1059;
xVel = -Math.floor(Math.random()*10);
}
else if(r<.75)//top wall
{
y=-41;
yVel = Math.floor(Math.random()*10);
}
else//bottom wall
{
y=641;
yVel = -Math.floor(Math.random()*10);
}
tag.xPos = x;
tag.style.left=x;
tag.yPos = y;
tag.style.top=y;
tag.style.xVel=xVel;
tag.style.yVel=yVel;
let t = tag;
setTimeout(function() {step(t);},200);
}
function step(tag)
{
var x = tag.xPos;
var y = tag.yPos;
var dx = tag.xVel;
var dy = tag.yVel;
x+=dx;
y+=dy;
let t = tag;
if(x<-60 || x>1060 || y<-42 || y>642)
{
x=-500;
y=-500;
tag.xPos=x;
tag.yPos=y;
tag.style.left=x;
tag.style.top=y;
setTimeout(function() {restartMe(t);},1000);
return;
}
tag.xPos=x;
tag.yPos=y;
tag.style.left=x;
tag.style.top=y;
setTimeout(function() {step(t);},200);
}
function startGame()
{
var tag = document.getElementById("game");
target = Object.keys(image)[Math.floor(Math.random()*Object.keys(image).length)];
var r = target;
while(r.indexOf('_')>-1)
{
r=r.replace('_',' ');
}
target=r;
tag.innerHTML="Look for the "+target;
}
function clickTag(id)
{
////HERE IS WHERE THE MOUSE EVENT SHOULD EXECUTE////
if(id===target)
{
startGame();
}
var tag = document.getElementById("output");
tag.innerHTML="No, that is the "+id;
}
</script>
</head>
<body onload="createAll();startGame()">
<h2>What do you see?</h2>
<p id="game"></p>
<p id="output"></p>
<p id="canvas" class="black" width="1000" height="600"></p>
</body>
</html>
Okay, here' the run down. I start with several image file names in an array with a key that identifies the image. When the page loads I go through the process of creating set of moveable p tags containing the image and a click event. There is a timeout for each of these tags and then they move across the screen. When one of them reaches a boundary it waits for a second and then starts again from one of the margins. This part works fine, but the mouse event doesn't.
I keep looking for a mouse event when I click on one of those things, but nothing is happening. I have tried putting the event on the p and the img. I have tried variations of onmousedown, onmouseup, onclick, etc. and nothing seems to work. I'm probably missing something obvious, so I'd appreciate a second set of eyes.

First addEventListener does not use "on" in the string
t.addEventListener("mousedown", ...
Now you add all the other events correctly and you call closures with your timeouts, but you build the click event wrong.
t.onclick="clickTag('"+r+"')";
That is assigning a string to the event listener, it is not binding a funciton call.
t.onclick= function () { clickTag(r); };

There's only one instance of createAll in your example, but its shown as a function declaration, so you are never actually executing createAll(), so it's not enabling your event listeners.
Try changing createOne() to createAll().
<body onload="createOne();startGame()">

Related

How to make clearInterval() work in JavaScript

I want to make an element (id=runner) move across the page by n pixels after a mouseover event, then stop at a certain position (left = 2000px), using setInterval() to repeatedly call move_left(), then clearInterval() when left == 200px. I can make the element move, but when I look in developer tools it never stops - left continues to increase. I am pretty new to JavaScript/HTML/CSS. How do I make it stop?
Relevant code:
<script>
function runner_go()
{
var load_time = performance.now();
const go = setInterval(move_left,20);
}
function move_left()
{
document.getElementById('runner').style.visibility = "visible";
var runner_position = getComputedStyle(document.getElementById('runner')).getPropertyValue('left');
document.getElementById('runner').style.left = parseInt(runner_position,10) + 17 + "px";
if (parseInt(runner_position,10) > 2000)
{
clearInterval(go);
}
}
</script>
</head>
<body style="background-color:gray;" onmouseover = "runner_go();">
<div>
<h1>Running!</h1>
</div>
<img src="images/runner_l.png" alt ="running man" style="position:relative; visibility:hidden;" id = "runner"/>
</body>
You need to create the var 'go' outside the method cause of the scope, also if you let on the 'body' the 'onmouseover' it will set the interval everytime.
Try this code to test:
<head>
<script>
let go = null;
function runner_go()
{
var load_time = performance.now();
go = setInterval(move_left,20);
}
function move_left()
{
document.getElementById('runner').style.visibility = "visible";
var runner_position = getComputedStyle(document.getElementById('runner')).getPropertyValue('left');
document.getElementById('runner').style.left = parseInt(runner_position,10) + 17 + "px";
if (parseInt(runner_position,10) > 2000)
{
clearInterval(go);
}
}
</script>
</head>
<body style="background-color:gray;" onclick = "runner_go();">
<div>
<h1>Running!</h1>
</div>
<img src="images/runner_l.png" alt ="running man" style="position:relative; visibility:hidden;" id = "runner"/> </body>
Problem -
You declared the interval variable as a constant within another function which is not accessible by the move_left function
So just move your interval variable to global scope (outside the function) and it should work
let go;
function runner_go() {
var load_time = performance.now();
go = setInterval(move_left, 20);
}
function move_left() {
document.getElementById('runner').style.visibility = "visible";
var runner_position = getComputedStyle(document.getElementById('runner')).getPropertyValue('left');
document.getElementById('runner').style.left = parseInt(runner_position, 10) + 17 + "px";
if (parseInt(runner_position, 10) > 2000) {
clearInterval(go);
}
}
sample on how intervals and clearIntervals work
let interval, i = 1;
function start() {
interval = setInterval(log, 1000);
}
function log() {
if (i >= 5) clearInterval(interval);
console.log(`Test ${i}`);
i++
}
start();

Understanding Functions and the semantics

Okay, Please be gentle I am learning Javascript.
I have two buttons (HTML5 <button> tags) they are both set to call the same function, In that function is two other functions (show + hide), Now, I am wondering if this is correct, i.e If i click on button hide, it calls function visibility, which then calls the function hide and hides everything defined in this function, including the original buttons. But if i click on button show, it hides the original content and displays extra content, with more buttons.(clickable process throughout)Or is it possible to call these functions separately within this function, For example:
function visible() {
function show() {
slide = document.getElementById('side');
pos = 0;
move = setInterval(slider, 1000/60);
slide1 = document.getElementById('main');
pos1 = 100;
move1 = setInterval(slider1, 1000/60);
document.getElementById('welcomer').style.display = 'none';
document.getElementById('welcome').style.display = 'none';
function slider() {
if (pos == 20) {
clearInterval(move);
}else {
pos++;
slide.style.display = 'block';
slide.style.width = pos + '%';
}
}
function slider1() {
if (pos1 == 80) {
clearInterval(move1);
}else {
pos1--;
slide1.style.width = pos1 + '%';
}
}
}
function hide() {
slide = document.getElementById('side');
pos = 0;
move = setInterval(slider, 1000/60);
slide1 = document.getElementById('main');
pos1 = 100;
move1 = setInterval(slider1, 1000/60);
document.getElementById('welcomer').style.display = 'none';
document.getElementById('welcome').style.display = 'none';
function slider() {
if (pos == 20) {
clearInterval(move);
}else {
pos++;
slide.style.display = 'block';
slide.style.width = pos + '%';
}
}
function slider1() {
if (pos1 == 80) {
clearInterval(move1);
}else {
pos1--;
slide1.style.width = pos1 + '%';
}
}
}
document.getElementById('vis').addEventListener('click', show());
document.getElementById('invis').addEventListener('click', hide());
}
Or is it semantically correct not to have nested functions, Instead to have the two separate functions, and call the functions separately onclick. For example:
function show() {
slide = document.getElementById('side');
pos = 0;
move = setInterval(slider, 1000/60);
slide1 = document.getElementById('main');
pos1 = 100;
move1 = setInterval(slider1, 1000/60);
document.getElementById('welcomer').style.display = 'none';
document.getElementById('welcome').style.display = 'none';
function slider() {
if (pos == 20) {
clearInterval(move);
}else {
pos++;
slide.style.display = 'block';
slide.style.width = pos + '%';
}
}
function slider1() {
if (pos1 == 80) {
clearInterval(move1);
}else {
pos1--;
slide1.style.width = pos1 + '%';
}
}
}
function hide() {
slide = document.getElementById('side');
pos = 0;
move = setInterval(slider, 1000/60);
slide1 = document.getElementById('main');
pos1 = 100;
move1 = setInterval(slider1, 1000/60);
document.getElementById('welcomer').style.display = 'none';
document.getElementById('welcome').style.display = 'none';
function slider() {
if (pos == 20) {
clearInterval(move);
}else {
pos++;
slide.style.display = 'block';
slide.style.width = pos + '%';
}
}
function slider1() {
if (pos1 == 80) {
clearInterval(move1);
}else {
pos1--;
slide1.style.width = pos1 + '%';
}
}
}
Or is it possible, for the button hide, do i just create an array, with every ID/Class Element i want to hide, call it with the onclick on the button to hide and then change the style to display: none; for all elements in that array?.
And vice versa for the divs i would create with button show?
Now i understand alot of this can be deemed opinionated, So the reason i am asking is this: I want to know which would be the quickest method using vanilla javascript, which way would shorten the amount of code and reduce loading time, with the best possible outcome based on facts.
Please note I have had to write this code with pretty much zero knowledge as i am learning Javascript, These are primarily for example, and both show/hide do the exact same thing for now, ultimately the show function will create more divs and content etc.
Having nested functions is perfectly acceptable, and often desirable or even necessary. The syntax would be as follows for doing so :
function base_function(){
this.function_1 = function(){};
this.function_2 = function(){};
}
This defines a function constructor with two values... function_1 and function_2. You could then call either of them using the following
var base = new base_function();
base.function_1();
Now, if you only need to reference those child functions within the scope of the base_function, something like the following would suffice.
function base_function(){
function child_function_1(){}
function child_function_2(){child_function_1();}
}
Think of function as it is in js, a datatype, like a string or numeric type. You can pass it to variables, reference it later on in code, pass it as a callback (like in setTimeout, you pass the reference to the function as a varaible. i.e. setTimeout(function_1,1000) rather than setTimeout(function_1(),1000)). You can also declare functions using var function_1 = function(){} because of this (and is my preferred method).
Because of the way javascript handles functions, you can do some interesting and super cool stuff with them - such as closures or IIFYs! Here is a great explanation of these and how to use them!
To answer your question, I would suggest going with the first route, while retaining a variable inside the function referencing the content you want to display/hide. Even if it means you need to write a few extra lines, it makes the code much more readable when you keep things modular, and readability really is king in most circumstances.
Something like this
var Vis_Controller = function(content){
this.content = content;
this.hide = function(){
content.style.display = "none";
}
this.show = function(){
content.style.display = "block";
}
}
window.addEventListener("load", function(){
var content = document.getElementById("content");
var button_show = document.getElementById("button-show");
var button_hide = document.getElementById("button-hide");
var vis_controller = new Vis_Controller(content);
button_show.addEventListener("click",vis_controller.show);
button_show.addEventListener("click",vis_controller.hide);
});

Changing picture frame with toggle button

Ok so we have a simple user info editing page.I want to create a toggle button which swaps profile picture border-radius from 0 to 25 and backwards.But the 2nd part doesnt work.I created if to check if the boarder radius is 25 already so it will make it 0, but it does not work.Here is my code
<!DOCTYPE html>
<html>
<head>
</head>
<body onload="buildImage();">
<div class="contents" id="content"></div>
<button onclick="changeImage()">NextImage</button>
<button onclick="changeShape()">ChangeShape</button>
<button onclick="uploadImage()">Upload Image</button>
</body>
<script>
var images = [ 'profile1.png', 'profile2.png','profile3.png'];
var index = 0;
var array_length = 3;
function buildImage() {
var img = document.createElement('img')
img.src = images[index];
document.getElementById('content').appendChild(img);
}
function changeImage(){
var img = document.getElementById('content').getElementsByTagName('img')[0]
index++;
index = index % array_length;
img.src = images[index];
}
function changeShape(){
var shape = document.getElementById('content').getElementsByTagName('img')[0].style.borderRadius = "25px";
if(shape.style.borderRadius == 25){
var shape2 = document.getElementById('content').getElementsByTagName('img')[0].style.borderRadius = "0px";
}
}
function uploadImage() {
images.push("profile4.png");
array_length++;
}
</script>
</html>
Any ideas why it doesnt work?
You are uselessly assigning variables in your function by the looks of it.
There is no need to declare shape2. Just declare shape once and then use that to check. Also make sure to check shape.style.borderRadius against a string like "25px" as that will be returned.
Try something like this:
function changeShape(){
var shape = document.getElementById('content').getElementsByTagName('img')[0];
if(shape.style.borderRadius == "25px"){
shape.style.borderRadius = "0px";
}else{
shape.style.borderRadius = "25px";
}
}

getelementbyid().click not working

I am making a website that whenever I click and x is equal to a range of numbers, it plays audio, but after i click once when x is between those numbers, i will also play if x is not in the range. Please Help. Here is my code. by the way, i will not use jquery because it does not work on chrome.
if(x<=1200&&x>=600){
var n=true;
};
if(x<=1200&&x>=600&&n==true){
document.getElementById('a').onclick = function(){
audio.play();
n=false;
}
}
else{n=false}
What your code does is that it adds a click event listener on that element, and afterwards the click handler will trigger regardless of your x value because you don't have any checks inside the click handler.
The solution is to do create the click listener only once, and check for the value of x inside it. Something like this:
document.getElementById('a').onclick = function(){
if(x <= 1200 && x >= 600 && n == true) {
audio.play();
n = false;
}
}
See this (I've replaced audio playing with div highlighting, but it's the same principle):
var r = document.getElementById('r');
var n = true;
var x = 1000;
document.getElementById('a').onclick = function(){
if(x <= 1200 && x >= 600 && n == true) {
r.className = 'playing';
n = false;
printN();
setTimeout(function(){
r.className = '';
}, 2000);
}
}
function toggleN(){n = !n;printN()}
function printN(){document.getElementById('n').textContent = 'n is set to ' + n;}
function randomizeX(){x = Math.floor(Math.random() * 1200);printX()}
function printX(){document.getElementById('x').textContent = 'x equals ' + x;}
#r {display: inline-block; width: 50px; height: 50px; background: #ccc; transition: background 1s linear;}
#r.playing {background: green}
<button id="a">Hit me</button><br><br>
<div id="r"></div>
<p id="x">x equals 1000</p>
<p id="n">n is set to true</p>
<button onclick="toggleN()">Toggle n</button>
<button onclick="randomizeX()">Randomize x</button>
I'm not quite sure what you're trying to do but here a guest :
if(x<=1200&&x>=600){
var n=true;
var clicked = false;
};
if(x<=1200&&x>=600&&n==true || clicked==true){
document.getElementById('a').onclick = function(){
audio.play();
n=false;
clicked = true;
}
}
else{
n=false;
clicked = false;
}
This should do what you want, if i understood you correctly.
no reason to check x several times + check n, since n is only true if x is between your ranges.
var ab = document.getElementById('a');
var n = false;
if( x <= 1200 && x >= 600 ){
n = true; //If x is within range.
} else {
n = false; //Reset when x goes out of range.
}
ab.onclick = function(){
if(n){
audio.play();
}
}
Note: If a user clicks the button several times,
the audio plays several times.
This can be fixed by adding another variable checking if a user already clicked once,
Since i don't know your setup, i.e. should users be able to click to play the audio several times in a row(when it finish playing once), i can't supply you with a solution for that without knowing.

How do I in vanilla javascript: selectors,events and the need of $(this)

I have 3 pictures cropped by span.main{overflow:hidden}. User can pan the span with touch events and explore the hidden parts of the picture.
Code so far:
document.addEventListener('DOMContentLoaded', function() {
var box = document.querySelector('.main');
box.addEventListener("touchstart", onStart, false);
box.addEventListener("touchmove", onMove, false);
box.addEventListener("touchend", onEnd, false);
});
var startOffsetX, startOffsetY;
var moving = false;
function getPos(ev) {
return {
x: ev.touches ? ev.touches[0].clientX : ev.clientX,
y: ev.touches ? ev.touches[0].clientY : ev.clientY
};
}
function onStart(ev) {
moving = true;
var box = document.querySelector('.main');// I need something like $(this)
var pos = getPos(ev);
startOffsetX = pos.x + box.scrollLeft;
startOffsetY = pos.y + box.scrollTop;
if (ev.preventDefault)
ev.preventDefault();
else
ev.returnValue = false;
}
function onMove(ev) {
if (moving) {
var pos = getPos(ev);
var x = startOffsetX - pos.x;
var y = startOffsetY - pos.y;
var box = document.querySelector('.main'); // I need something like $(this)
box.scrollLeft = x;
box.scrollTop = y;
if (ev.preventDefault)
ev.preventDefault();
else
ev.returnValue = false;
}
}
function onEnd(ev) {
if (moving) {
moving = false;
}
}
The problem is that only the first thumbnail works as expected. I've tried:
-querySelector only returns the first element so if I add ID's and querySelector('#box1,#box2,#box3') should work. Nein. I thing I have a 'this' problem on the functions...
-Place events (as Apple suggests) inline <div class="box" onStart="ontouchstartCallback( ev);" ontouchend="onEnd( ev );"ontouchmove="onMove( ev );" > <img></div> looked like a solution yet...My guess, because of 'this' again...
You want to use the querySelectorAll method instead. It returns all matched elements in the subtree instead of only the first one (which is what querySelector does). Then loop through them using a for loop.
var elements = document.querySelectorAll('.main');
for (var i = 0, ii = elements.length; i < ii; ++i) {
var element = elements[i];
element.ontouchstart = onStart;
// ...
}
The other approach you can take (and it is probably a better one) is to use event delegation and set the event listeners on a parent element and decide which of the pictures is being manipulated by checking the target property of each event.
<div id="pictures">
<span class="main"><img /></span>
<span class="main"><img /></span>
<span class="main"><img /></span>
</div>
var parent = document.getElementById('pictures');
parent.ontouchstart = function (e) {
var box = e.target.parentNode; // parentNode because e.target is an Image
if (box.className !== 'main') return;
onStart(e, box);
};

Categories