How do I loop through an array of buttons and only continue looping if the user clicks the next button in the array - javascript

Not sure if this is exactly the correct approach for this but I have an array of buttons that spawn on the screen and after a few seconds it changes location and I have to click the buttons in the order they appeared initially.
Following is the code:
let arrayButtons = [];
let goButton = document.getElementById("inputButton");
function Buttons(color, height, width, top, left, order) {
this.order = order;
this.btn = document.createElement("button");
this.btn.style.backgroundColor = color;
this.btn.style.height = height;
this.btn.style.width = width;
this.btn.style.position = "absolute";
document.body.appendChild(this.btn);
this.setLocation = function(top, left) {
this.btn.style.top = top;
this.btn.style.left = left;
};
this.setLocation(top, left);
}
function createButtons(numOfButtons) {
console.log(numOfButtons);
let color;
let heightVal;
let widthVal;
let top;
let left;
let currentButton;
for (let i = 0; i < numOfButtons; i++) {
color = "#" + genRandomColor();
heightVal = "60px";
widthVal = "120px";
let x = window.innerWidth - 100;
let y = window.innerHeight - 100;
top = 90 + "px";
left = i * 120 + "px";
currentButton = new Buttons(color, heightVal, widthVal, top, left, i);
arrayButtons.push(currentButton);
}
}
function genRandomColor() {
let randomColor = Math.floor(Math.random() * 16777215).toString(16);
return randomColor;
}
function change() {
setTimeout(function() {
let x = window.innerWidth - 100;
let y = window.innerHeight - 200;
for (let i = 0; i < arrayButtons.length; i++) {
randomX = Math.floor(Math.random() * x + 50);
randomY = Math.floor(Math.random() * y + 50);
arrayButtons[i].setLocation(
randomX + 'px',
randomY + 'px');
}
}, 5000);
}
function isValid(range) {
range = document.getElementById("textField").value;
if (range < 2 || range > 10) {
return false;
} else {
return true;
}
}
// idea.. if the user clicks on the button in the order of the array it works
// for the hint, just make the value equal to the index number + 1
function clickHandler() {
let minMax = isValid();
if (minMax == true) {
createButtons(document.getElementById("textField").value);
change();
} else {
window.alert("Must be between 2 and 10");
}
}
goButton.onclick = clickHandler;
<p>How Many Buttons To Create?</p>
<input id="textField" type="text">
<button type="button" id="inputButton">Go!</button>
<br>
<br>

HTML
<button class="buttonClass">1</button>
JS
let btns = document.querySelectorAll(".buttonClass");
let currentBtn = 0;
btns.forEach(function(btn, crtIndex){
btn.onclick = function(){
if(currentBtn === crtIndex){
currentBtn++;
console.log("correct");
}
};
});

Related

Random movement of circles created by the script

I have a function that craeates divs with a circle.
Now they are all created and appear at the beginning of the page and go further in order.
Next, I need each circle to appear in a random place. I did this.
Now I need all of them to move randomly across the entire page, I have difficulties with this.
Here is an example of how everything works for one element that is already on the page.
https://jsfiddle.net/quej8wko/
But when I add this code, all my created circles don't move.
I get an error:
"message": "Uncaught TypeError: Cannot set properties of null (setting 'willChange')",
This is probably due to the fact that initially there are no circles on the page. How can I connect the code so that all created circles move?
//creating circles
var widthHeight = 40; // <-- circle width
var margin = 20; // <-- margin - is it necessary ?
var delta = widthHeight + margin;
function createDiv(id, color) {
let div = document.createElement('div');
var currentTop = 0;
var documentHeight = document.documentElement.clientHeight;
var documentWidth = document.documentElement.clientWidth;
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.backgroundColor = color;
}
div.classList.add("circle");
div.classList.add("animation");
// Get the random positions minus the delta
currentTop = Math.floor(Math.random() * documentHeight) - delta;
currentLeft = Math.floor(Math.random() * documentWidth) - delta;
// Keep the positions between -20px and the current positions
var limitedTop = Math.max(margin * -1, currentTop);
var limitedLeft = Math.max(margin * -1, currentLeft);
div.style.top = limitedTop + "px";
div.style.left = limitedLeft + "px";
document.body.appendChild(div);
}
let i = 0;
const oneSecond = 1000;
setInterval(() => {
i += 1;
createDiv(`circle${i}`)
}, oneSecond);
//move circles
function RandomObjectMover(obj, container) {
this.$object = obj;
this.$container = container;
this.container_is_window = container === window;
this.pixels_per_second = 250;
this.current_position = { x: 0, y: 0 };
this.is_running = false;
}
// Set the speed of movement in Pixels per Second.
RandomObjectMover.prototype.setSpeed = function(pxPerSec) {
this.pixels_per_second = pxPerSec;
}
RandomObjectMover.prototype._getContainerDimensions = function() {
if (this.$container === window) {
return { 'height' : this.$container.innerHeight, 'width' : this.$container.innerWidth };
} else {
return { 'height' : this.$container.clientHeight, 'width' : this.$container.clientWidth };
}
}
RandomObjectMover.prototype._generateNewPosition = function() {
// Get container dimensions minus div size
var containerSize = this._getContainerDimensions();
var availableHeight = containerSize.height - this.$object.clientHeight;
var availableWidth = containerSize.width - this.$object.clientHeight;
// Pick a random place in the space
var y = Math.floor(Math.random() * availableHeight);
var x = Math.floor(Math.random() * availableWidth);
return { x: x, y: y };
}
RandomObjectMover.prototype._calcDelta = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt( dx*dx + dy*dy );
return dist;
}
RandomObjectMover.prototype._moveOnce = function() {
// Pick a new spot on the page
var next = this._generateNewPosition();
// How far do we have to move?
var delta = this._calcDelta(this.current_position, next);
// Speed of this transition, rounded to 2DP
var speed = Math.round((delta / this.pixels_per_second) * 100) / 100;
//console.log(this.current_position, next, delta, speed);
this.$object.style.transition='transform '+speed+'s linear';
this.$object.style.transform='translate3d('+next.x+'px, '+next.y+'px, 0)';
// Save this new position ready for the next call.
this.current_position = next;
};
RandomObjectMover.prototype.start = function() {
if (this.is_running) {
return;
}
// Make sure our object has the right css set
this.$object.willChange = 'transform';
this.$object.pointerEvents = 'auto';
this.boundEvent = this._moveOnce.bind(this)
// Bind callback to keep things moving
this.$object.addEventListener('transitionend', this.boundEvent);
// Start it moving
this._moveOnce();
this.is_running = true;
}
RandomObjectMover.prototype.stop = function() {
if (!this.is_running) {
return;
}
this.$object.removeEventListener('transitionend', this.boundEvent);
this.is_running = false;
}
// Init it
var x = new RandomObjectMover(document.querySelector(".circle"), window);
// Start it off
x.start();
.circle {
clip-path: circle(50%);
height: 40px;
width: 40px;
margin: 20px;
position: absolute;
}
I have modified the snippet which works as you expected.
There was a mistake where you were initializing and creating the object instance only once and none of the div elements that you created inside the setInterval function never got Instantiated.
I think you are just starting out with JavaScript with this sample project.
Below are few suggestions:
Learn to debug the code. You should be using dev tools by making use of debugger statement where it takes you to the source code to analyze the variable scope and stack during the runtime. console.log also helps in few situations.
I could see a lot of confusing naming convention (You have named the create div parameter as id but creating a div class using that id)
Try using ES6 features (class syntax is really good when writing OOP in JS although it's just a syntactic sugar for prototype)
//creating circles
var widthHeight = 40; // <-- circle width
var margin = 20; // <-- margin - is it necessary ?
var delta = widthHeight + margin;
function createAndInitializeDivObject(id, color) {
let div = document.createElement('div');
var currentTop = 0;
var documentHeight = document.documentElement.clientHeight;
var documentWidth = document.documentElement.clientWidth;
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.backgroundColor = color;
}
div.classList.add("circle");
div.classList.add("animation");
// Get the random positions minus the delta
currentTop = Math.floor(Math.random() * documentHeight) - delta;
currentLeft = Math.floor(Math.random() * documentWidth) - delta;
// Keep the positions between -20px and the current positions
var limitedTop = Math.max(margin * -1, currentTop);
var limitedLeft = Math.max(margin * -1, currentLeft);
div.style.top = limitedTop + "px";
div.style.left = limitedLeft + "px";
document.body.appendChild(div);
var x = new RandomObjectMover(document.querySelector(`.${id}`), window);
x.start();
}
let i = 0;
const oneSecond = 1000;
setInterval(() => {
i += 1;
createAndInitializeDivObject(`circle${i}`)
}, oneSecond);
//move circles
function RandomObjectMover(obj, container) {
this.$object = obj;
this.$container = container;
this.container_is_window = container === window;
this.pixels_per_second = 250;
this.current_position = { x: 0, y: 0 };
this.is_running = false;
}
// Set the speed of movement in Pixels per Second.
RandomObjectMover.prototype.setSpeed = function(pxPerSec) {
this.pixels_per_second = pxPerSec;
}
RandomObjectMover.prototype._getContainerDimensions = function() {
if (this.$container === window) {
return { 'height' : this.$container.innerHeight, 'width' : this.$container.innerWidth };
} else {
return { 'height' : this.$container.clientHeight, 'width' : this.$container.clientWidth };
}
}
RandomObjectMover.prototype._generateNewPosition = function() {
// Get container dimensions minus div size
var containerSize = this._getContainerDimensions();
var availableHeight = containerSize.height - this.$object.clientHeight;
var availableWidth = containerSize.width - this.$object.clientHeight;
// Pick a random place in the space
var y = Math.floor(Math.random() * availableHeight);
var x = Math.floor(Math.random() * availableWidth);
return { x: x, y: y };
}
RandomObjectMover.prototype._calcDelta = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt( dx*dx + dy*dy );
return dist;
}
RandomObjectMover.prototype._moveOnce = function() {
// Pick a new spot on the page
var next = this._generateNewPosition();
// How far do we have to move?
var delta = this._calcDelta(this.current_position, next);
// Speed of this transition, rounded to 2DP
var speed = Math.round((delta / this.pixels_per_second) * 100) / 100;
//console.log(this.current_position, next, delta, speed);
this.$object.style.transition='transform '+speed+'s linear';
this.$object.style.transform='translate3d('+next.x+'px, '+next.y+'px, 0)';
// Save this new position ready for the next call.
this.current_position = next;
};
RandomObjectMover.prototype.start = function() {
if (this.is_running) {
return;
}
// Make sure our object has the right css set
this.$object.willChange = 'transform';
this.$object.pointerEvents = 'auto';
this.boundEvent = this._moveOnce.bind(this)
// Bind callback to keep things moving
this.$object.addEventListener('transitionend', this.boundEvent);
// Start it moving
this._moveOnce();
this.is_running = true;
}
RandomObjectMover.prototype.stop = function() {
if (!this.is_running) {
return;
}
this.$object.removeEventListener('transitionend', this.boundEvent);
this.is_running = false;
}
// Init it
var x = new RandomObjectMover(document.querySelector(".circle"), window);
// Start it off
x.start();
.circle {
width: 35px;
height: 35px;
border-radius: 35px;
background-color: #ffffff;
border: 3px solid purple;
position: absolute;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="circle"></div>
<script src="app.js"></script>
</body>
</html>

I wanna repeat function infinite but i couldn't

I wanna do some effect to my background so i tought i can add some shapes flying around but i do only 1 shape i cant loop or anything
i tried call function more than a one time but it doesnt help
function animasyon(a) {
window.onload=function(){
var id = setInterval(anim,5);
$('body').append('<div class=shape></div>');
$('body').append('<div class=shape></div>');
var kutu = document.getElementsByClassName("shape");
var pos = 0;
var x = window.innerWidth;
var y = window.innerHeight;
var borderSize = Math.floor(Math.random() * 3 ) + 1;
var ySize = Math.floor(Math.random() * y ) + 1;
var Size = Math.floor(Math.random() * 30 ) + 5;
var yon = Math.floor(Math.random() *2)+1;
var dolu = Math.floor(Math.random() *2)+1;
if (ySize > 50) { ySize-=20; }
function anim(){
if (pos == x) {
clearInterval(id);
document.getElementById("shape").remove();
}else{
pos++;
kutu[a].style.position = "absolute";
kutu[a].style.border = "solid rgb(119,38,53) "+borderSize+"px";
kutu[a].style.left = pos+"px";
kutu[a].style.width = Size+"px";
kutu[a].style.height = Size+"px";
if (yon == 1) { ySize-=0.2; } else { ySize+=0.2; }
if (dolu==1) {kutu[a].style.background = "rgb(119,38,53)";}
if (kutu[a].offsetTop < 0 || kutu[a].offsetTop > y-30) {document.getElementById("shape").remove();}
kutu[a].style.top = ySize+"px";
}
}
}
}
animasyon(0);
Try Calling 'anim' function in this way
setInterval(function(){anim()},5);
Your problem is that you are assigning window.onload function a value inside the function animasyon
window.onload holds only 1 function. If you call animation function more than once, then the last one will overwrite the first one.
Edit: You need to separate your animation logic from the page load logic. They are completely separate things.
Here is an example of adding multiple objects and animating each separately.
// HTML
<div id="container">
<div id="ball"></div>
<div id="ball2"></div>
<div id="ball3"></div>
<div id="ball4"></div>
</div>
// CSS
#ball, #ball2, #ball3, #ball4 {
background: red;
border: 1px solid #FAFDFA;
display: inline-block;
width: 1em;
height: 1em;
border-radius: 2em;
position: absolute;
}
#ball2 {
background: blue;
}
#ball3 {
background: green;
}
#ball4 {
background: purple;
}
#container {
width: 512px;
height: 512px;
background: black;
position: relative;
}
// JS
const container = document.getElementById('container');
const stageWidth = 512;
const stageHeight = 512;
const makeFall = (elementId) => {
// this function makes an enlement fall in random direction
const animationSpeed = 4;
// your onload function
const ball = document.getElementById(elementId);
let directionX = (Math.random() * animationSpeed * 2) - animationSpeed;
let directionY = Math.random() * animationSpeed;
const setRandomStart = () => {
ball.style.top = '10px';
ball.style.left = (Math.random() * (stageWidth / 2)) + 'px';
directionX = (Math.random() * animationSpeed * 2) - animationSpeed;
directionY = Math.random() * animationSpeed;
}
setRandomStart();
let animationInterval = setInterval(() => {
let px = parseFloat(ball.style.left);
let py = parseFloat(ball.style.top);
px += directionX;
py += directionY;
if (px > stageWidth - 20 || py > stageHeight - 20 || px < -20) {
setRandomStart();
} else {
ball.style.left = px + 'px';
ball.style.top = py + 'px';
}
}, 48);
}
// In Your onload function you can add the elements and then animate
makeFall('ball');
makeFall('ball2');
makeFall('ball3');
makeFall('ball4');
https://jsfiddle.net/753oL8re/4/

How to collide using OOP JavaScript?

I am making a game(shooting game) using OOP javascript for my project. I already moved(player) and make the enemy disappear at the edge of the container. Now, I want to collide with the shooter(player) and enemy and display the message "Game Over". Here is my code of enemy.js and shooter.js. Also, my container.js.
container.js
class Container{
constructor(){
this.containerElement = document.getElementsByClassName("container")[0];
}
initialization(){
this.shooterElement = document.getElementsByClassName("shooter")[0];
let containerElement = document.getElementsByClassName("container")[0];
this.backgroundElement = document.getElementsByClassName("background")[0];
this.background = new Background(this.backgroundElement);
this.shooter = new Shooter(this.shooterElement);
document.addEventListener('keydown', (e)=>this.shooter.buttonGotPressed(e));
let enemyElement = document.getElementsByClassName("enemy")[0];
this.enemies = [];
setInterval(function(){
var top = Math.floor(Math.random() * 50) + 1;
var left = Math.floor(Math.random() * 550) + 1;
this.enemy = new Enemy({parentElement: containerElement, leftPosition: left, topPosition: top});
},400)
this.enemies.push(this.enemy);
}
}
let container = new Container();
container.initialization();
enemy.js
class Enemy{
constructor({parentElement,leftPosition, topPosition }){
let enemyElement = document.createElement("div");
this.enemyElement = enemyElement;
this.leftPosition = leftPosition;
this.topPosition = topPosition;
this.containerHeight = 600;
this.y = 15;
this.height = 40;
this.width = 50;
this.enemyElement.style.left = this.leftPosition + 'px';
this.enemyElement.style.top= this.topPosition + 'px';
this.enemyElement.style.height = this.height + 'px';
this.enemyElement.style.width = this.width + 'px';
this.enemyElement.style.position = "absolute";
this.enemyElement.style.background="url(images/enemy3.png)";
console.log(enemyElement)
parentElement.appendChild(this.enemyElement);
this.containerCollision = this.containerCollision.bind(this);
setInterval(this.containerCollision, 100);
}
containerCollision(){
if(this.topPosition >= this.containerHeight - this.width ){
this.enemyElement.style.display="none";
}
this.topPosition = this.topPosition + this.y;
this.enemyElement.style.top = this.topPosition + 'px';
}
}
shooter.js (this is the player)
class Shooter {
constructor(shooterElement){
this.shooterElement = shooterElement;
this.shooterleftPosition = 300;
this.shootertopPosition = 555;
this.containerWidth = 600;
this.width = 30;
this.height = 40;
this.x = 5;
this.y = 1;
this.shooterElement.style.left = this.shooterleftPosition + 'px';
this.shooterElement.style.top = this.shootertopPosition + 'px';
this.buttonGotPressed = this.buttonGotPressed.bind(this);
}
buttonGotPressed(e){
console.log(e);
if(e.key == "ArrowLeft"){
console.log(e.key);
if(this.shooterleftPosition <= 0){
this.shooterleftPosition = 0;
}
else
{
this.shooterleftPosition = this.shooterleftPosition - this.x;
}
console.log(this.shooterleftPosition)
this.shooterElement.style.left = this.shooterleftPosition +'px';
}
if(e.key == "ArrowRight"){
this.shooterleftPosition = this.shooterleftPosition + this.x;
this.shooterElement.style.left = this.shooterleftPosition +'px';
if(this.shooterleftPosition >= this.containerWidth - this.width){
this.shooterleftPosition = this.containerWidth - this.width;
this.shooterElement.style.left = this.leftPosition + 'px';
}
}
}
}
Now, How do I detect and collide shooter(player) and enemy and display the message/alert "Game Over". If the enemy touches the shooter(player). Here is pic too.
How do i know enemy touched the player and display game over in this pic
What I tried to detect the collision of player and enemy
collidingShooter(){
if (this.enemies.push(this.enemy)>1)
{
(
([this.enemies][this.enemy].top <= this.shootertopPosition + 50) &&
([this.enemies][this.enemy].top >= this.shootertopPosition) &&
([this.enemies][this.enemy].left >= this.shooterleftPosition) &&
([this.enemies][this.enemy].left <= this.shooterleftPosition+ 50)
)
console.log("hitttt");
this.enemies.splice(this.enemy,1);
// this.shooterElement.splice(1);
}
}
I believe that you messed something up here
First of all you use array in the wrong way:
[this.enemies][this.enemy].top
What it does:
Creates new array with one element (which is this.enemies array), now it converts this.enemy to a string = 'object Object' and tries to find a key in this new array, which propably returns undefined and now it tries to get top of undefined.
What you probably wanted to do is:
this.enemies[this.enemies.indexOf(this.enemy)].top
But even though it has no oop sense.
What I would do:
function checkCollision() {
for (let i = 0; i < this.enemies.length; i++) {
if (areColliding(this.shooter, this.enemies[i])) {
console.log('hit')
}
}
}
function areColliding(o1, o2) {
if (o1.top <= o2.top + o2.height &&
o1.top >= o2.top &&
o1.left <= o2.left + o2.width &&
o1.left >= o2.left) {
return true
}
return false
}
There are better collision algorithms than aabb and even if you want to do this in this way, your game can get very slow if you have thousands of enemies. There is algorithm to split game world in to smaller rectangles and check for collision inside them.

Display Absolute positioned images inside a div

i am trying to absolute position images at random positions inside a div. i'm not sure how to get the calculations right for 'top' and 'left' but the images on occasions display outside of the div. i want to also prevent the overlapping of the images.
Any ideas would help
(function() {
//array of links to the images
var images = ["http://via.placeholder.com/150/800",
"http://via.placeholder.com/150/080",
"http://via.placeholder.com/150/008",
"http://via.placeholder.com/150/880"
];
//function to calculate a random integer
var getRandomInt = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
//function to get a top and left value position for each image
var pos = function() {
var wrapWidth = document.getElementById("wrap");
wrapWidth = $("#wrap").width();
wrapHeight = $("#wrap").height();
// Image Position
var xPos = getRandomInt(0, wrapWidth - 150);
var yPos = getRandomInt(0, wrapHeight - 150);
return {
x: xPos + "px",
y: yPos + "px"
}
}
var displayImages = function(images) {
var elementArray = [];
for (var i = 0; i < images.length; i++) {
var src = images[i];
elementArray[i] = '<img class="imagePos" style="top:' + pos().x + '; left:' + pos().y + ' " src="' + src + ' "/>';
}
console.log(elementArray);
elementArray.forEach(function(element) {
console.log(element);
$("#wrap").append(element);
});
}
displayImages(images);
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="IntTree">
<div id="header">
<h1>Interactive Tree</h1>
</div>
<div id="wrap">
</div>
</div>
I'm assuming that you have some css resembling this:
img {
position: absolute;
width:150;
height:150;
}
Regarding your first issue, you appear to have your x and y assignments backwards in the bit where you're adding the elems to the array. Also, you're making 2 times as many calls to pos() as needed.
That line should be:
let position = pos();
elementArray[i] = '<img class="imagePos" style="top:'+position.y+'; left:'+position.x+' " src="'+src+' "/>';
For the second issue, you need to check for each image whether any of the corners overlap a different image. The easy way to achieve this by adding an array to track the positions you've already used, and comparing against the items in the array for subsequent position calculations.
(function (){
//array of links to the images
var images = ["http://via.placeholder.com/150/800",
"http://via.placeholder.com/150/080",
"http://via.placeholder.com/150/008",
"http://via.placeholder.com/150/880"
];
//function to calculate a random integer
var getRandomInt = function (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// array to track previous positions of images
var positions = [];
//function to get a top and left value position for each image
var pos = function (){
var wrapWidth = $("#wrap").width();
var wrapHeight = $("#wrap").height();
// Image Position
var xPos = getRandomInt(0, wrapWidth - 150);
var yPos = getRandomInt(0, wrapHeight - 150);
var overlapX = true;
var overlapY = true;
while(overlapX && overlapY) {
overlapX = false;
overlapY = false;
for(var i = 0; i < positions.length; i++) {
// check if x coord is inside previously placed image
if ( (xPos > positions[i].x && xPos < positions[i].x+150) ||
(xPos+150 > positions[i].x && (xPos+150) < positions[i].x+150) ){
overlapX = true;
}
// check if y coord is inside previously placed image
if( (yPos > positions[i].y && yPos < positions[i].y+150) ||
(yPos+150 > positions[i].y && yPos+150 < positions[i].y+150) ) {
overlapY = true;
}
}
if (overlapX) {
xPos = getRandomInt(0, wrapWidth - 150);
}
if (overlapY) {
yPos = getRandomInt(0, wrapHeight - 150);
}
}
positions.push({x:xPos,y:yPos});
return {
x: xPos + "px",
y: yPos + "px"
}
}
var displayImages = function(images){
var elementArray = [];
for (var i = 0; i < images.length; i++) {
var src = images[i];
let position = pos();
elementArray[i] = '<img class="imagePos" style="top:'+position.y+'; left:'+position.x+' " src="'+src+' "/>';
}
elementArray.forEach(function(element) {
$("#wrap").append(element);
});
}
displayImages(images);
})();

JavaScript rectangle follow cursor in menu

I wrote a code in JS for my future site and it's all fine but the only bad thing here is in menu the rectangle (div) moves with brakes. I mean it must be like in a site some features of which I want to have on my own. http://lusens.ru/ There on main menu the rectangle mouse very smoothly. I can't understand why in my case it doesn't happen.
This is a part of html
<body>
<ul >
<li id="a1" onmouseover="highlightMenu('a1')">Первый пункт</li>
<li id="a2" onmouseover="highlightMenu('a2')">Второй пункт точка</li>
<li id="a3" onmouseover="highlightMenu('a3')">Третьий пункт точка и запятая</li>
<li id="a4" onmouseover="highlightMenu('a4')">Четвёртый пункт</li>
</ul>
<div id="d1"></div>
and the JS file
function highlightMenu(id) {
time = 0;
var rect = document.getElementById(id).getBoundingClientRect();
var width = document.getElementById(id).offsetWidth;
var idTop = rect.top;
var idLeft = rect.left;
var rect1 = document.getElementById('d1').getBoundingClientRect();
var shadowWidth = document.getElementById('d1').offsetWidth;
var shadowLeft = rect1.left;
var shadowTop = rect1.top;
if (shadowLeft < idLeft) {
for (i = shadowLeft, time = 50; i < idLeft - 3; i++, time += 5) {
setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);
}
} else {
for (i = shadowLeft, time = 50; i > idLeft - 3; i--, time += 5) {
setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);
}
}
if (shadowWidth < width) {
for (i = shadowWidth; i < width + 10; i++, time += 0.01) {
setTimeout("document.getElementById('d1').style.width='" + i + "px'", time);
}
} else {
for (i = shadowWidth; i > width + 10; i--, time += 0.01) {
setTimeout("document.getElementById('d1').style.width='" + i + "px'", time);
}
}
if (shadowLeft < idLeft) {
for (i = idLeft + 3; i < idLeft + 20; i++, time += 25) {
setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);
}
for (i = idLeft + 20; i > idLeft - 5; i--, time += 50) {
setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);
}
} else {
for (i = idLeft - 3; i > idLeft - 20; i--, time += 25) {
setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);
}
for (i = idLeft - 20; i < idLeft - 5; i++, time += 50) {
setTimeout("document.getElementById('d1').style.left='" + i + "px'", time);
}
}
}
http://jsfiddle.net/m2SBm/
Using the onmouseover causes to fire the event handler function multiple times, here is better to use the onmouseenter event. And also instead of scheduling 50 timeouts, rather use single timeout and global variables, so that the animation will be stoppable.
HTML:
<ul id="menu">
<li>Первый пункт</li>
<li>Второй пункт точка</li>
<li>Третьий пункт точка и запятая</li>
<li>Четвёртый пункт</li>
</ul>
<div id="d1"></div>
Javascript:
var rect, rect1; // rectangles
var shadow; // shadow div
var bpos; // begin position
var epos; // end position
var width, shadowWidth;
var step; // animation step 1..50
var timer = null;
function animateRect() {
step++;
if (step > 50) {
clearInterval(timer);
timer = null;
return;
}
var t = bpos.t + Math.round((epos.t - bpos.t) * step / 50);
var l = bpos.l + Math.round((epos.l - bpos.l) * step / 50);
var w = shadowWidth + Math.round((width - shadowWidth) * step / 50);
shadow.style.top = t + "px";
shadow.style.left = l + "px";
shadow.style.width = w + "px";
}
function highlightMenu(e) {
e = e || window.event; // for IE8,7 compatibility
var item = e.target || e.srcElement; // for IE8,7
if (timer) {
clearInterval(timer);
}
step = 0;
rect = item.getBoundingClientRect();
width = item.offsetWidth;
epos = {
t: rect.top,
l: rect.left
};
rect1 = shadow.getBoundingClientRect();
shadowWidth = shadow.offsetWidth;
bpos = {
t: rect1.top,
l: rect1.left
};
timer = setInterval(animateRect, 5);
}
function init() {
var menu = document.getElementById('menu');
var items = menu.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
items[i].onmouseenter = highlightMenu;
}
shadow = document.getElementById('d1');
shadow.style.width = items[0].offsetWidth + 'px';
shadow.style.top = items[0].getBoundingClientRect().top + 'px';
shadow.style.left = items[0].getBoundingClientRect().left + 'px';
}
if (window.addEventListener) {
window.addEventListener('load', init, false);
} else if (window.attachEvent) {
window.attachEvent('onload', init);
}
JSFiddle: http://jsfiddle.net/m2SBm/6/ (using the getBoundingClientRect function)
Update:
To make your menu work correctly also with eventual scrollbars, compute the element offset position rather than using the getBoundingClientRect function as shown in the following answer:
finding element's position relative to the document
Here is additional CSS for transparency in menu:
ul#menu, ul#menu li {
position:relative;
background-color:transparent;
}
#d1 {
z-index:-10;
}
The complete menu here: http://jsfiddle.net/m2SBm/8/

Categories