Javascript is having problems with moving divs around - javascript

Here's a function:
function rollImgOnSwipe(direction) {
changeDirection(direction);
rollImage(direction);
}
The first called function (changeDirection(direction);) is supposed to place a div in either the left or right side of the screen, depending on the value of its argument. The second one (rollImage(direction);) is to make the div move from left to right or from right to left, depending on where the first function placed it.
Well, it seems like it first calls rollImage and then changeDirection because it takes rollImgOnSwipe to be called more than once in order for the div to be placed on the other side.
The best way to explain this to you is to show you - run the code snippet and try switching between the divs with the prev and next buttons and see how they behave.
var nextBtn = document.getElementById('next'),
prevBtn = document.getElementById('prev'),
shown = document.getElementsByClassName('shown')[0],
hidden = document.getElementsByClassName('hidden')[0];
function rollImage(direction) {
shown.classList.remove("shown");
shown.classList.add("hidden");
hidden.classList.remove("hidden");
hidden.classList.add("shown");
shown = document.getElementsByClassName('shown')[0];
hidden = document.getElementsByClassName('hidden')[0];
changeDirection(direction);
}
function rollImgOnSwipe(direction) {
changeDirection(direction);
rollImage(direction);
}
function changeDirection(direction) {
hidden.style.left = (105 * direction) + "%";
shown.style.left = 0;
}
nextBtn.addEventListener('click', function () { rollImgOnSwipe(1); });
prevBtn.addEventListener('click', function () { rollImgOnSwipe(-1); });
.img {
position: absolute;
display: block;
width: 80px;
height: 30px;
border-style: solid;
border-color: red;
border-width: 5px;
}
.shown{
left: 0;
transition: left .6s ease, transform .6s ease;
}
.hidden{
left: 105%;
}
.img-wrapper{
position: relative;
width: 80px;
height: 30px;
padding: 30px;
white-space: nowrap;
overflow: hidden;
}
<button id="prev">prev</button>
<div class="img-wrapper">
<div class="img shown"></div>
<div class="img hidden"></div>
</div>
<button id="next">next</button>
To fix this, i make the compiler wait for a bit before calling rollImage by using the setTimeout function in rollImgOnSwipe.
And now the program works as expected:
var nextBtn = document.getElementById('next'),
prevBtn = document.getElementById('prev'),
shown = document.getElementsByClassName('shown')[0],
hidden = document.getElementsByClassName('hidden')[0];
function rollImage(direction) {
shown.classList.remove("shown");
shown.classList.add("hidden");
hidden.classList.remove("hidden");
hidden.classList.add("shown");
shown = document.getElementsByClassName('shown')[0];
hidden = document.getElementsByClassName('hidden')[0];
changeDirection(direction);
}
function rollImgOnSwipe(direction) {
changeDirection(direction);
setTimeout(function() { rollImage(direction); }, 50);
}
function changeDirection(direction) {
hidden.style.left = (105 * direction) + "%";
shown.style.left = 0;
}
nextBtn.addEventListener('click', function(){ rollImgOnSwipe(1); });
prevBtn.addEventListener('click', function(){ rollImgOnSwipe(-1); });
.img {
position: absolute;
display: block;
width: 80px;
height: 30px;
border-style: solid;
border-color: red;
border-width: 5px;
}
.shown{
left: 0;
transition: left .6s ease, transform .6s ease;
}
.hidden{
left: 105%;
}
.img-wrapper{
position: relative;
width: 80px;
height: 30px;
padding: 30px;
white-space: nowrap;
overflow: hidden;
}
<button id="prev">prev</button>
<div class="img-wrapper">
<div class="img shown"></div>
<div class="img hidden"></div>
</div>
<button id="next">next</button>
My question is:
What is the problem and why is it fixed by making the compiler wait?

Related

Several elements use the same css class, how can I change the css properties of only one of those elements

I am creating a little game that should be like a 2d version of "guitar hero" (if you don't know what "guitar hero" is don't worry, it was just to give context). I have a red square creator function called squareCreator that adds each new square created a CSS class of .newMostLeftNote. Afterward, I want each one of those squares to fall down (like gravity) using the function fallingMostLeftNote. The problem is that the margin-top that function adds to the square generated by the squareCreator adds to every single square at the same time (even before the square is created), so a square could be created when the .newMostLeftNote CSS class has a margin-top of 700 and it appears way at the bottom.
How can I make it so that every square that falls, but starts falling after they appear?
Notice that in this image, every margin-top CSS property for every new generated square is exactly the same.
var mostLeftNoteMarginTop = 0;
function squareCreator(){
var newNote = document.createElement("div");
newNote.className = "newMostLeftNote";
document.body.appendChild(newNote);
}
var generationSpeed = setInterval(squareCreator, 300);
function fallingMostLeftNote() {
mostLeftNoteMarginTop += 2;
$(".newMostLeftNote").css({
'margin-top': mostLeftNoteMarginTop + 'px'
});
}
proc = setInterval(fallingMostLeftNote, 5);
.newMostLeftNote {
height: 100px;
width: 100px;
background-color: red;
margin-left: 400px;
position: absolute;
}
.mostLeftNote {
height: 100px;
width: 100px;
background-color: red;
margin-left: 300px;
position: absolute;
}
.middleNote {
height: 100px;
width: 100px;
background-color: blue;
margin-left: 600px;
position: absolute;
}
.mostRightNote {
height: 100px;
width: 100px;
background-color: green;
margin-left: 900px;
position: absolute;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Score: 0</h1>
<div class="middleNote"></div>
<div class="mostLeftNote"></div>
<div class="mostRightNote"></div>
<div class="scoreLineTop"></div>
<div class="scoreLineButtom"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="main.js" charset="utf-8"></script>
</body>
Update
var squareQuantity = [];
function squareCreator(){
var newNote = document.createElement("div");
newNote.className = "newMostLeftNote";
document.body.appendChild(newNote);
squareQuantity.push(this.newNote);
}
var generationSpeed = setInterval(squareCreator, 300);
function fallingMostLeftNote() {
mostLeftNoteMarginTop += 2;
squareQuantity[2].css({
'margin-top': mostLeftNoteMarginTop + 'px'
});
}
Instead of using javascript to update your margin-top, you could use CSS animations. Each new square will animate independently.
Here's an example for your use case:
function addSquare() {
var squaresElement = document.getElementById("squares");
var squareElement = document.createElement("div");
squareElement.className = "square";
squaresElement.append(squareElement);
}
#squares {
display: flex;
}
.square {
height: 100px;
width: 100px;
margin-right: 10px;
background-color: red;
animation-name: fall;
animation-duration: 4s;
animation-timing-function: linear;
}
/* The animation code */
#keyframes fall {
from {margin-top: 0px;}
to {margin-top: 300px;}
}
<button onclick="addSquare()">Add square</button>
<div id="squares"></div>
My approach is giving a css variable while creating divs for transform delay. If you need more complex movements, you can use the same logic for animation instead of transform.
<div class="parent"></div>
.parent {
display: flex;
flex-direction: row;
align-items: flex-start;
}
.lets-try {
flex: 1;
background: #000;
height: 60px;
margin-right: 5px;
margin-left: 5px;
transition: all 0.5s ease-in-out var(--delay);
}
.lets-try.is-falling {
margin-top: 100px;
}
let parent = document.querySelector(".parent");
let numOfSquares = 12;
for (let i = 0; i < numOfSquares ; i++) {
let delay = i * 0.2;
let div = document.createElement('div');
div.setAttribute('class', 'lets-try');
div.setAttribute('style', `--delay:${delay}s`);
parent.appendChild(div);
}
setTimeout(() => {
let items = document.querySelectorAll(".lets-try");
[...items].forEach(item => {
item.classList.add("is-falling")
})
}, 1)

Uncaught TypeError: Cannot read property 'addEventListener' of null (querySelector)

I'm getting the following error:
Uncaught TypeError: Cannot read property 'addEventListener' of null
addEventListener works perfectly fine on a single id (menu)... Is there a restriction that I can't use it on querySelector?
(Yes, JavaScript is at the bottom of the HTML document)
Any help will be appreciated.
https://plnkr.co/edit/AIAOZk40ssoofpvrt9dm?p=preview
var menu = document.getElementById("menu");
var area = document.querySelector("#menu + #envelope + #links");
menu.addEventListener("mouseenter", addHref);
area.addEventListener("mouseleave", remHref);
menu.addEventListener("click", addHref);
document.addEventListener("click", function (){
if (this != area){
remHref();
}
});
function remHref (){
document.getElementById("google").removeAttribute("href");
document.getElementById("mysite").removeAttribute("href");
}
function addHref (){
setTimeout(activate, 2500);
}
function activate (){
document.getElementById("google").setAttribute("href", "https://www.google.com");
document.getElementById("mysite").setAttribute("href", "https://www.mywebsite.com");
}
<div id="menu">Click here to browse the internet.
<div id="envelope">
<div id="links" >
<div><a ><img id="google" src="https://seomofo.com/wp-content/uploads/2010/05/google_logo_new.png" /></a></div>
<div style="width: 20%;"></div>
<div><a ><img id="mysite" src="https://toppng.com/uploads/preview/wwf-logo-horizontal-world-wildlife-foundation-logo-shirt-11563219164hg5hfcveei.png"/></a></div>
</div>
</div>
</div>
#menu{
height: 10vh;
background-color: red;
text-align: center;
transition: all 1s ease-out;
padding-top: 5vh;
}
#menu:hover{
color: red;
}
#envelope{
height: 0;
display: block;
visibility: hidden;
background-color: blue;
min-width: 100%;
z-index: 1;
position: absolute;
left: 0;
content: "";
opacity: 0;
transition: all 1.3s ease-out;
}
#links{
height: 0;
visibility: hidden;
display: flex;
background-color: pink;
justify-content: center;
z-index: 2;
min-width: 100%;
opacity: 0;
transition: all 1s ease-in;
}
#google{
margin-top: -1vh;
width: 150px;
}
#mysite{
padding-left: 5%;
margin-top: -1vh;
width: 150px;
}
#menu:hover #envelope{
height: 100px;
opacity: 1;
visibility: visible;
}
#menu:focus #envelope{
height: 100px;
opacity: 1;
visibility: visible;
}
#menu:hover #links{
opacity: 1;
height: 300px;
visibility: visible;
}
#menu:focus #links{
opacity: 1;
height: 300px;
visibility: visible;
}
Try changing:
var area = document.querySelector("#menu + #envelope + #links");
for:
var area = document.querySelector("#menu #envelope #links");
As your html is:
<div id="menu">Click here to browse the internet.
<div id="envelope">
<div id="links" >
...
</div>
</div>
</div>
The element+element selector (Adjacent Sibling Selector) is used to select elements that are placed immediately after (not inside) the first specified element. You need the Descendant Selector or the Child Selector.
Have a look at the w3cschools CSS Combinators.
Or, in this case and as epascarello says, you can just use:
var area = document.querySelector("#links");
as the ids must be unique if the document validates.
i've edited your plunk to fit the 2 small mistake you've made.
The first mistake was to include the script in the tag.
i've put in before the closing tag
Also, as mentionned #jeprubio you need to remove the '+' from the querySelector() call.
Here is the edited plunk
https://plnkr.co/edit/sB7r0MguCN3KvhdHvB4i
code
// Code goes here
var menu = document.getElementById("menu");
var area = document.querySelector("#menu #envelope #links");
menu.addEventListener("mouseenter", addHref);
area.addEventListener("mouseleave", remHref);
menu.addEventListener("click", addHref);
document.addEventListener("click", function (){
if (this != area){
remHref();
}
});
function remHref (){
document.getElementById("google").removeAttribute("href");
document.getElementById("mysite").removeAttribute("href");
}
function addHref (){
setTimeout(activate, 2500);
}
function activate (){
document.getElementById("google").setAttribute("href", "https://www.google.com");
document.getElementById("mysite").setAttribute("href", "https://www.mywebsite.com");
}
Your code is fine you need just to :
Place your script before closing tag </body>.
Correct querySelector fo element area from "#menu + #envelope + #links" to "#menu #envelope #links"
Example :
// Code goes here
var menu = document.getElementById("menu");
var area = document.querySelector("#menu #envelope #links");
menu.addEventListener("mouseenter", addHref);
area.addEventListener("mouseleave", remHref);
menu.addEventListener("click", addHref);
document.addEventListener("click", function (){
if (this != area){
remHref();
}
});
function remHref (){
document.getElementById("google").removeAttribute("href");
document.getElementById("mysite").removeAttribute("href");
}
function addHref (){
setTimeout(activate, 2500);
}
function activate (){
document.getElementById("google").setAttribute("href", "https://www.google.com");
document.getElementById("mysite").setAttribute("href", "https://www.mywebsite.com");
}
/* Styles go here */
#menu{
height: 10vh;
background-color: red;
text-align: center;
transition: all 1s ease-out;
padding-top: 5vh;
}
#menu:hover{
color: red;
}
#envelope{
height: 0;
display: block;
visibility: hidden;
background-color: blue;
min-width: 100%;
z-index: 1;
position: absolute;
left: 0;
content: "";
opacity: 0;
transition: all 1.3s ease-out;
}
#links{
height: 0;
visibility: hidden;
display: flex;
background-color: pink;
justify-content: center;
z-index: 2;
min-width: 100%;
opacity: 0;
transition: all 1s ease-in;
}
#google{
margin-top: -1vh;
width: 150px;
}
#mysite{
padding-left: 5%;
margin-top: -1vh;
width: 150px;
}
#menu:hover #envelope{
height: 100px;
opacity: 1;
visibility: visible;
}
#menu:focus #envelope{
height: 100px;
opacity: 1;
visibility: visible;
}
#menu:hover #links{
opacity: 1;
height: 300px;
visibility: visible;
}
#menu:focus #links{
opacity: 1;
height: 300px;
visibility: visible;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div id="menu">Click here to browse the internet.1
<div id="envelope">
<div id="links">
<div>
<a><img id="google" src="https://seomofo.com/wp-content/uploads/2010/05/google_logo_new.png" /></a>
</div>
<div style="width: 20%;"></div>
<div>
<a><img id="mysite" src="https://toppng.com/uploads/preview/wwf-logo-horizontal-world-wildlife-foundation-logo-shirt-11563219164hg5hfcveei.png" /></a>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Wrap your Javascript in window.onload()
window.onload=function() {
/*your code here*
/*var date = document.getElementById("date");
/*alert(date);
}
It looks like the browser is executing your JavaScript before it has "executed your HTML"/made the page, so those HTML elements/DOM nodes don't exist yet. This introduces a delay until the HTML is loaded, so that the nodes exist (not null) before JavaScript tries to attach methods to them. (And what jprubio said below about your selector.)

How to make a div go over another div when clicking on it?

I have these 2 divs and when I click on div 1 I want it to go over the second div, and if I click on Div 1 again I want it to go back to its original position (I want Div 1 to increase its width so it goes over the second Div). Here is my code where I have my 2 divs next to each other. Can anyone point me in the right direction on how to accomplish this? Thanks a lot in advance!
NOTE:
- No jQuery please. I'm trying to accomplish this with javascript and css.
#parent {
display: flex;
}
#narrow {
width: 200px;
background: lightblue;
}
#wide {
flex: 1;
background: lightgreen;
}
<div id="parent">
<div id="wide">Div 1</div>
<div id="narrow">Div 2</div>
</div>
If you're willing to ditch flex, you can use a combination of float , postion:absolute and transition so that the main div "slides over" the other div
document.querySelector("#wide").onclick = toggleWidth;
function toggleWidth() {
this.classList.toggle("active");
}
#parent {
position: relative;
}
#narrow {
width: 200px;
background: lightblue;
float: right;
}
#wide {
position: absolute;
background: lightgreen;
width: calc(100% - 200px);
transition: width 2s;
}
#wide.active {
width: 100%;
opacity: 0.9;
}
<div id="parent">
<div id="wide">Div 1</div>
<div id="narrow">Div 2</div>
</div>
Note: Changing the opacity is purely optional, I've only done it to further illustrate the "slide over" effect.
Try this
#parent {
display: flex;
}
#narrow {
width: 20vw;
position: absolute;
left: calc(80vw - 10px);
background: lightblue;
z-index: 1;
margin: 0;
}
#wide {
width: calc(80vw - 10px);
background: lightgreen;
transition: all 0.3s ease-out;
}
.wider {
width: 100vw!important;
z-index: 2;
}
<div id="parent">
<div id="wide" onclick="myFunction()">Div 1</div>
<div id="narrow">Div 2</div>
</div>
<script>
function myFunction() {
var element = document.getElementById("wide");
element.classList.toggle("wider");
}
</script>
You can try it using JavaScript.
First, you prepare your CSS:
#narrow {
width: 200px;
transition: 0.32s;
overflow: hidden;
}
#wide.fullwidth ~ #narrow {
width: 0;
opacity: 0;
}
Then, the JavaScript, like this:
document.querySelector("#wide").onclick = changeDivWidth;
var wideFull = false;
function changeDivWidth () {
if (!wideFull) {
this.classList.add("fullwidth");
wideFull = true;
return; // if variable wideFull is false, function stops here
}
wideFull = false;
this.classList.remove("fullwidth");
}
Shorter approach using toggle();
document.querySelector("#wide").onclick = changeDivWidth;
function changeDivWidth () {
this.classList.toggle("fullwidth");
}
Are you looking for something like this : JSFiddle ?
JavaScript (Pure) :
function HideDivOne(){
var wide = document.getElementById("wide");
var narrow = document.getElementById("narrow");
if (wide.style.width == "70%"){
wide.style.width = "100%";
narrow.style.width = "0%";
narrow.style.opacity = "0";
}
else{
wide.style.width = "70%";
narrow.style.width = "30%";
narrow.style.opacity = "1";
}
}
CSS
#parent {
display: flex;
}
#narrow {
width: 30%;
background: lightblue;
height: 20px;
transition: 0.2s;
}
#wide {
width: 70%;
flex: 1;
background: lightgreen;
height: 20px;
transition: 0.2s;
}
HTML
<div id="parent">
<div id="wide" onclick="HideDivOne()">Div1</div>
<div id="narrow" onclick="HideDivTwo()">Div2</div>
</div>
You can change the z-index of the divs based on your desired effect. My suggestion is using jQuery. On click on div 1 add a class to the div that modify the zindex, that is, if the class is not already added, if so, remove it.

How to properly animate a bar gliding?

I have two buttons, when a user clicks on them it gets underlined. However, I'd like the .underline to be animated/glide horizontally to the button that is being clicked on.
Demo: https://jsfiddle.net/ds1wr736/11/
As of right now, the .underline just appears and disapears when a button is clicked. How can I animate this to smoothly glide (x values changing) to the selected button without hacks and JQuery?
function switchTab(tab) {
if (tab === 1) {
document.getElementById("tab2").classList.add("underline");
document.getElementById("tab1").classList.remove("underline");
}
else if (tab === 2) {
document.getElementById("tab1").classList.add("underline");
document.getElementById("tab2").classList.remove("underline");
}
}
.bar {
background-color: gray;
padding: 20px;
}
.underline {
border-bottom: 5px solid red;
}
button {
width: 100px;
height: 50px;
background-color: white;
}
button:focus {
outline: none;
}
<div class="bar">
<button id='tab1' class="underline" onclick='switchTab(2)'>Tab 1</button>
<button id='tab2' onclick='switchTab(1)'>Tab 2</button>
</div>
Rather than animating a border I've created an additional element that reacts to the the click events. This allows us to track the position of the "underline" and scale and animate it between buttons when clicked.
This can be modified to accept hover events instead using mouseover instead of click.
let buttons = document.querySelectorAll('button');
buttons.forEach(button => {
button.addEventListener('mouseover', hoverboard); // Hover event
//button.addEventListener('click', hoverboard);
});
function hoverboard(e) {
const board = document.querySelector('.hoverboard');
// - 1 due to the border of the button
let width = this.offsetWidth - 1;
const firstChild = document.querySelector('.bar button:first-child');
const lastChild = document.querySelector('.bar button:last-child');
// - 19 due to padding being 20px on the left and removing 1 for the button's border
let left = this.offsetLeft - 19;
board.style.cssText = 'transform: translateX(' + left + 'px); width: ' + width + 'px;';
}
.bar {
position: relative;
background-color: gray;
padding: 20px;
}
.underline {
border-bottom: 5px solid red;
}
button {
width: 100px;
height: 50px;
background-color: white;
}
button:focus {
outline: none;
}
.hoverboard {
position: absolute;
width: 100px;
height: 3px;
background: red;
transition: transform .25s ease, width .25s ease;
}
<div class="bar">
<button id='tab1'>Tab 1</button>
<button id='tab2' style="width: 65px;">Tab 2</button>
<button>Tab 3</button>
<div class="hoverboard"></div>
</div>
Here ya go. Only the edited classes are here:
.underline:after {
border-bottom: 5px solid red;
animation-name: slideIn;
animation-duration: 1s;
width: 100%;
content: '';
position: absolute;
bottom: 0;
left: 0;
}
#keyframes slideIn {
from {width: 0;}
to {width: 100%;}
}
button{
position: relative;
width: 100px;
height: 50px;
background-color: white;
}
What I did is that I used the abstract after element on the buttons and positioned it absolute to it's relative button. And used css animation.

Jquery animate() effect doesn't function well

When hover on the first and second element, some element will animate to the left, it works well if hovered with a normal speed, but will crashed if hovered too fast for some times
(the text won't show or the text won't move back to its original place when mouseoff, checkout the figures below).
Any suggestions would be appreciated.
1.text won't show
2.text won't move back to its original place
$(document).ready(function() {
var flag = false;
$(".tab-ico").hover(function() {
var f = $(this);
f.data('timeout', window.setTimeout(function() {
f.find(".tab-text").stop(true, true).animate({
left: "-=64"
}, 300, function() {
flag = true;
});
}, 300));
}, function() {
clearTimeout($(this).data("timeout"));
if (flag === true) {
$(this).find(".tab-text").stop(true, true).animate({
left: "+=64"
}, 300, function() {
flag = false;
});
}
});
});
.pfm-toolbar-wrap {
height: 100%;
position: fixed;
right: 0;
top: 0;
width: 35px;
z-index: 9990;
}
.pfm-tbar-tab-Spike {
position: relative;
width: 35px;
}
.pfm-toolbar-tabs {
border-right: 5px solid #7a6e6e;
height: 100%;
}
.p-tab div.tab-ico {
background: #7a6e6e;
}
.tab-text {
border-radius: 3px;
color: #fff;
height: 32px;
left: 0px;
line-height: 32px;
position: absolute;
text-align: center;
width: 70px;
padding-right: 5px;
z-index: -1;
background: #7a6e6e;
}
.tab-text a {
color: #fff;
display: block;
}
.p-tab {
left: 0;
margin-top: -100px;
position: absolute;
top: 50%;
width: 35px;
z-index: 9;
text-align: center;
}
.p-tab div.tab-ico:hover {
background: #e20531;
cursor: pointer;
}
.p-tab div.tab-ico:hover .tab-text {
background: #e20531;
}
.tab-ico {
width:35px;
height:35px;
margin-bottom:5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="pfm-toolbar-wrap">
<div class="pfm-toolbar-tabs">
<div class="p-tab">
<div class="pfm-tbar-tab-Spike m_b15">
<div class="tab-ico cart"> <i class="cbl-icon"></i> <em class="tab-text"> text</em>
</div>
</div>
<div class="pfm-tbar-tab-group m_b15">
<div class="tab-ico "> <i class="cbl-icon"></i>
<em class="tab-text"> text2</em>
</div>
</div>
</div>
</div>
</div>
you can use css transition-delay property as follows:
transition-delay: 1s; /* delays for 1 second */
-webkit-transition-delay: 1s; /* for Safari & Chrome */
Find more info here.
I suggest that you use CSS transition, here are two links that will help you make that with less code and using CSS transition
https://css-tricks.com/almanac/properties/t/transition/
https://blog.alexmaccaw.com/css-transitions

Categories

Resources