I have a vertical navbar. When the client is on the website and viewing a section of the page that corresponds to the navbar button, that button should have a white border around it. When the client leaves that section for another section, that button should have a border around it and the last buttons border should disappear.
Unfortunately, only half of this works. I don't know why but when I scroll to another section the last sections corresponding button doesn't lose it's border, even though the debug messages state that the border color has been computed to be 'transparent'.
I've tried setting all faces of the border to transparent (top, bottom, left, right) and I've tried setting the style is jquery.
$(document).ready(() => {
$('.page').css('height', $(window).height());
$('.navbar').css('height', $(window).height());
})
let currentActiveButton = null;
document.addEventListener("scroll", () => {
let ids = calculateVisiblePages();
console.log(ids.join(", ") + "\n");
let heights = getVisibleHeights(ids);
let entry;
let highest = -1;
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
if (highest == -1) {
highest = heights[id];
entry = id;
continue;
}
let height = heights[id];
if (highest < height) {
highest = height;
entry = id;
}
}
// console.log(`Highest: ${entry}`);
if (currentActiveButton === entry) return;
if (currentActiveButton != null) {
console.log(
`Attempting to set current active button, id is ${currentActiveButton}, to transparent.`
);
let activeButton = document.getElementById(currentActiveButton);
activeButton.style.borderColor = 'transparent';
let computedStyle = window.getComputedStyle(activeButton);
console.log(`Computes style border color: ${computedStyle.borderTopColor}`);
}
currentActiveButton = entry;
let buttons = document.getElementsByClassName("navbar_button");
switch (entry) {
case "projects": {
console.log("Case is projects.");
borderButton("portfolioButton");
return;
}
case "previousComms": {
borderButton("previousCommsButton");
return;
}
case "aboutMe": {
borderButton("aboutMeButton");
return;
}
}
});
// function getCurrentActiveButton() {
// let buttons = document.getElementsByClassName("navbar_button");
// for (let i = 0; i < buttons.length; i++) {
// const button = buttons[i];
// let computed = window.getComputedStyle(button);
// if (computed.borderTopColor.startsWith("rgba(255, 255, 255")) {
// return button;
// }
// }
// }
function borderButton(id) {
let button = document.getElementById(id);
let computedStyle = window.getComputedStyle(button);
button.style.borderColor = "white";
}
function calculateVisiblePages() {
let pages = document.getElementsByClassName("page");
let visible = [];
for (let i = 0; i < pages.length; i++) {
const page = pages[i];
if (isVisible(page)) visible.push(page.id);
}
return visible;
}
function isVisible(element) {
let elementTop = element.offsetTop;
let elementBottom =
elementTop + Number(element.style.height.replace("px", ""));
let viewportTop = window.scrollY;
let viewportBottom = viewportTop + window.innerHeight;
return elementBottom > viewportTop && elementTop < viewportBottom;
}
function getVisibleHeights(ids) {
let cache = {};
for (let i = 0; i < ids.length; i++) {
// console.log(`Iterating on element: ${ids[i]}`);
const element = document.getElementById(ids[i]);
let elementTop = element.offsetTop;
let elementBottom =
elementTop + Number(element.style.height.replace("px", ""));
let viewportBottom = window.scrollY + window.innerHeight;
let bottom = elementBottom - viewportBottom;
if (bottom < 0) bottom = elementBottom;
if (bottom < viewportBottom && viewportBottom < elementBottom)
bottom = viewportBottom;
let top = elementTop > window.scrollY ? elementTop : window.scrollY;
cache[element.id] = bottom - top;
// for (let i = elementTop; i < elementBottom; i++) {
// //Check if pixel is in element and in the viewport.
// // console.log(`Iteration: ${i}`);
// if (i < window.scrollY) continue;
// if (i > window.scrollY && i < elementBottom && i < viewportBottom) {
// cache[element.id] = i - window.scrollY;
// }
// }
}
return cache;
}
The CSS:
#import url(https://fonts.googleapis.com/css?family=Roboto:500&display=swap);
.root {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 95% 5%
}
.pages {
display: grid;
grid-template-rows: auto auto auto
}
.page {
height: 100%;
width: 100%
}
#projects {
background: #00f
}
#previousComms {
background: #ff0
}
#aboutMe {
background: red
}
.navbar_buttons_wrapper {
height: 100%;
width: 5%;
position: fixed;
top: 0;
right: 0
}
.navbar_buttons {
height: 100%;
display: flex;
align-items: center;
justify-content: center
}
.navbar_buttons ul {
height: auto;
list-style: none;
color: #fff;
padding: 0;
display: grid;
grid-template-columns: auto auto auto;
transform: rotate(-90deg)
}
.navbar_buttons ul li {
width: max-content;
margin-left: 30px;
margin-right: 30px;
font-size: 1.2rem;
text-transform: uppercase;
border-style: solid;
border-color: transparent;
transition: .7s;
padding: 7px
}
html body {
font-family: Roboto, sans-serif;
margin: 0;
border: 0;
padding: 0;
background: #2c2c2c
}
The HTML:
<html>
<head>
<link rel="stylesheet" type="text/css" href="./styles/main.css" />
<script type="text/javascript" src="./scripts/main-min.js"></script>
</head>
<body>
<div class="root">
<div class="pages">
<div class="page" id="projects"></div>
<div class="page" id="previousComms"></div>
<div class="page" id="aboutMe"></div>
</div>
<div class="navbar">
<div class="navbar_buttons_wrapper">
<div class="navbar_buttons">
<ul>
<li class="navbar_button" id="portfolioButton">Portfolio</li>
<li class="navbar_button" id="previousCommsButton">Previous Commissioners</li>
<li class="navbar_button" id="aboutMeButton">About Me</li>
</ul>
</div>
</div>
</div>
</div>
</body>
</html>
I had expected the border to change from white to transparent. However, all I got was no change to the color of the border of the button of the section I had previously been looking at on the website:
Before Movement: https://gyazo.com/77171adefe255973709f11e305bfb030
After Movement: https://gyazo.com/b121d1d33b4f5f205df1468cd936352b
Source Before Movement: https://gyazo.com/92359267cf06cbe3b7c4942f04dbf9ea
Source After Movement: https://gyazo.com/7cc03865e17fc42382774747fb30052a
Github Project File: https://github.com/TheMasteredPanda/Portfolio-Website/blob/master/src/scripts/src/navbarBorderManagement.js#L32
Your problem is inconsistent naming conventions between your ID's and it's causing your element objects to get obfuscated, so if for example you were to breakpoint where you're setting your borderColor to reset it back to transparent, you'd see you're hitting a page HTMLDivElement and not the li you're aiming for. See changes below and have a great weekend, cheers!
$(document).ready(() => {
$('.page').css('height', $(window).height());
$('.navbar').css('height', $(window).height());
})
let currentActiveButton = null;
document.addEventListener("scroll", () => {
let ids = calculateVisiblePages();
console.log(ids.join(", ") + "\n");
let heights = getVisibleHeights(ids);
let entry;
let highest = -1;
for (let i = 0; i < ids.length; i++) {
const id = ids[i];
if (highest == -1) {
highest = heights[id];
entry = id;
continue;
}
let height = heights[id];
if (highest < height) {
highest = height;
entry = id;
}
}
// console.log(`Highest: ${entry}`);
if (currentActiveButton === entry) return;
if (currentActiveButton != null) {
console.log(
`Attempting to set current active button, id is ${currentActiveButton}, to transparent.`
);
let activeButton = document.getElementById(currentActiveButton + 'Button');
activeButton.style.borderColor = 'transparent';
let computedStyle = window.getComputedStyle(activeButton);
console.log(`Computes style border color: ${computedStyle.borderTopColor}`);
}
currentActiveButton = entry;
let buttons = document.getElementsByClassName("navbar_button");
switch (entry) {
case "projects": {
console.log("Case is projects.");
borderButton("projectsButton");
return;
}
case "previousComms": {
borderButton("previousCommsButton");
return;
}
case "aboutMe": {
borderButton("aboutMeButton");
return;
}
}
});
// function getCurrentActiveButton() {
// let buttons = document.getElementsByClassName("navbar_button");
// for (let i = 0; i < buttons.length; i++) {
// const button = buttons[i];
// let computed = window.getComputedStyle(button);
// if (computed.borderTopColor.startsWith("rgba(255, 255, 255")) {
// return button;
// }
// }
// }
function borderButton(id) {
let button = document.getElementById(id);
let computedStyle = window.getComputedStyle(button);
button.style.borderColor = "white";
}
function calculateVisiblePages() {
let pages = document.getElementsByClassName("page");
let visible = [];
for (let i = 0; i < pages.length; i++) {
const page = pages[i];
if (isVisible(page)) visible.push(page.id);
}
return visible;
}
function isVisible(element) {
let elementTop = element.offsetTop;
let elementBottom =
elementTop + Number(element.style.height.replace("px", ""));
let viewportTop = window.scrollY;
let viewportBottom = viewportTop + window.innerHeight;
return elementBottom > viewportTop && elementTop < viewportBottom;
}
function getVisibleHeights(ids) {
let cache = {};
for (let i = 0; i < ids.length; i++) {
// console.log(`Iterating on element: ${ids[i]}`);
const element = document.getElementById(ids[i]);
let elementTop = element.offsetTop;
let elementBottom =
elementTop + Number(element.style.height.replace("px", ""));
let viewportBottom = window.scrollY + window.innerHeight;
let bottom = elementBottom - viewportBottom;
if (bottom < 0) bottom = elementBottom;
if (bottom < viewportBottom && viewportBottom < elementBottom)
bottom = viewportBottom;
let top = elementTop > window.scrollY ? elementTop : window.scrollY;
cache[element.id] = bottom - top;
// for (let i = elementTop; i < elementBottom; i++) {
// //Check if pixel is in element and in the viewport.
// // console.log(`Iteration: ${i}`);
// if (i < window.scrollY) continue;
// if (i > window.scrollY && i < elementBottom && i < viewportBottom) {
// cache[element.id] = i - window.scrollY;
// }
// }
}
return cache;
}
#import url(https://fonts.googleapis.com/css?family=Roboto:500&display=swap);
.root {
width: 100%;
height: 100%;
display: grid;
grid-template-columns: 95% 5%
}
.pages {
display: grid;
grid-template-rows: auto auto auto
}
.page {
height: 100%;
width: 100%
}
#projects {
background: #00f
}
#previousComms {
background: #ff0
}
#aboutMe {
background: red
}
.navbar_buttons_wrapper {
height: 100%;
width: 5%;
position: fixed;
top: 0;
right: 0
}
.navbar_buttons {
height: 100%;
display: flex;
align-items: center;
justify-content: center
}
.navbar_buttons ul {
height: auto;
list-style: none;
color: #fff;
padding: 0;
display: grid;
grid-template-columns: auto auto auto;
transform: rotate(-90deg)
}
.navbar_buttons ul li {
width: max-content;
margin-left: 30px;
margin-right: 30px;
font-size: 1.2rem;
text-transform: uppercase;
border-style: solid;
border-color: transparent;
transition: .7s;
padding: 7px
}
html body {
font-family: Roboto, sans-serif;
margin: 0;
border: 0;
padding: 0;
background: #2c2c2c
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<head>
<link rel="stylesheet" type="text/css" href="./styles/main.css" />
<script type="text/javascript" src="./scripts/main-min.js"></script>
</head>
<body>
<div class="root">
<div class="pages">
<div class="page" id="projects"></div>
<div class="page" id="previousComms"></div>
<div class="page" id="aboutMe"></div>
</div>
<div class="navbar">
<div class="navbar_buttons_wrapper">
<div class="navbar_buttons">
<ul>
<li class="navbar_button" id="projectsButton">Portfolio</li>
<li class="navbar_button" id="previousCommsButton">Previous Commissioners</li>
<li class="navbar_button" id="aboutMeButton">About Me</li>
</ul>
</div>
</div>
</div>
</div>
</body>
</html>
Related
I made a multi section landing page with a navbar containing the links to the sections of the page
I need to give an active class to the link of the section closest to the top of the page, I tried using the scroll event listener but it is not working properly
//respond to scrolling by giving navbar active state for current section
``document.addEventListener("scroll", function() {
const scrollPos = document.body.scrollTop;
let pos = 0;
for (let i = 0; i < sec.length; i++) {
// determining pos of current section
pos+=sec[i].offsetHeight ;
let secPos = sec[i].getBoundingClientRect().y;
secPos - scrollPos <= 0 && secPos + pos >= scrollPos ? list[i].classList.add("active") : list[i].classList.remove("active")
}
});```
The scroll event is working properly, but your code does not correctly address the list of elements you are itering.
just try changing list[] to sec[]
document.addEventListener( "DOMContentLoaded", function() {
let sec = document.querySelectorAll('section');
console.log(sec);
sec[0].classList.add("active")
document.addEventListener("scroll", function() {
const scrollPos = document.body.scrollTop;
let pos = 0;
for (let i = 0; i < sec.length; i++) {
// determining pos of current section
pos+=sec[i].offsetHeight ;
let secPos = sec[i].getBoundingClientRect().y;
if ((secPos - scrollPos <= 0) && (secPos + pos >= scrollPos))
sec[i].classList.add("active");
else
sec[i].classList.remove("active");
}
})
})
section {
font-size: 80px;
text-align: center;
width: 300px;
margin: 20px auto;
background-color: pink;
border: 1px solid red;
}
section.active {
border: 1px solid blue;
background-color: cyan;
}
<section>1</section>
<section>2</section>
<section>3</section>
<section>4</section>
<section>5</section>
<section>6</section>
<section>7</section>
<section>8</section>
I'm having problems with a functionality that I have to implement, I'm looking to do this (check the second section) -> https://www.jacquemus.com/fr/simon
The letters need to fade in and fade out, based on the scroll, this is what I got so far
<div class="ml3">
<h1>THIS IS MY TEXT THAT IT'S GOING TO SHOW IN SCROLL</h1>
</div>
:root {
--percentage: 0;
}
body {
background-color: #000;
margin: 0;
height: 120vh;
}
.ml3 {
position: sticky;
top: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
span {
font-family: Helvetica;
margin: 0;
padding: 0;
font-size: 48px;
color: #fff;
letter-spacing: -0.3px;
}
.ml3 span{
opacity: var(--percentage);
}
var textWrapper = document.querySelector('.ml3');
textWrapper.innerHTML = textWrapper.textContent.replace(/\S/g, "<span class='letter'>$&</span>");
var letter = document.querySelectorAll('.letter');
var i = 0;
var currentID = 0;
var slideCount = letter.length;
document.addEventListener('scroll', (e) => {
let scrolled = document.documentElement.scrollTop / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
var nextID = currentID + 1;
if(nextID < slideCount){
letter[nextID].style.setProperty('--percentage', `${scrolled / 1}` * nextID);
}
currentID = nextID;
});
https://codepen.io/federicomartin/pen/eYdBbQm
As you can see, it's no near what I want, but I really don't know how to do it, if someone could help me, would be awesome! Thanks!
I like that effect a lot!! Thank for submitting that question! ;)
So here is a something to help you continue on this challenge.
In the scroll handler, I replaced:
var nextID = currentID + 1;
if(nextID < slideCount){
letter[nextID].style.setProperty('--percentage', `${scrolled / 1}` * nextID);
}
currentID = nextID;
with:
letter.forEach(function (l, i) {
if (i / letter.length < scrolled) {
l.style.setProperty("--percentage", 1);
}else{
l.style.setProperty("--percentage", 0);
}
});
It compares the scrolled percentage you calculated with the letter index "percentage" in the letter collection to set it's opacity to 0 or 1.
I would then adjust the scrollHeight of the body with the real text to reveal... Below, I used height: 600vh; and may be a bit too much. ;)
var textWrapper = document.querySelector(".ml3");
textWrapper.innerHTML = textWrapper.textContent.replace(
/\S/g,
"<span class='letter'>$&</span>"
);
var letter = document.querySelectorAll(".letter");
var i = 0;
var currentID = 0;
var slideCount = letter.length;
document.addEventListener("scroll", (e) => {
let scrolled =
document.documentElement.scrollTop /
(document.documentElement.scrollHeight -
document.documentElement.clientHeight);
// var nextID = currentID + 1;
// if (nextID < slideCount) {
// letter[nextID].style.setProperty(
// "--percentage",
// `${scrolled / 1}` * nextID
// );
// }
// currentID = nextID;
letter.forEach(function (l, i) {
// console.log("====",i / letter.length, i, letter.length)
if (i / letter.length < scrolled) {
l.style.setProperty("--percentage", 1);
} else {
l.style.setProperty("--percentage", 0);
}
});
});
:root {
--percentage: 0;
}
body {
background-color: #000;
margin: 0;
height: 1120vh;
}
.ml3 {
position: sticky;
top: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
span {
font-family: Helvetica;
margin: 0;
padding: 0;
font-size: 48px;
color: #fff;
letter-spacing: -0.3px;
}
.ml3 span {
opacity: var(--percentage);
}
<div class="ml3">
<h1>THIS IS MY TEXT THAT IT'S GOING TO SHOW IN SCROLL</h1>
</div>
CodePen
I have a graph that is rendering its values as a div inside the body element with a class according to their number values. This is working fine. But next I need to sort the divs according to their number values or background color. BUT, it needs to start on the lower left corner of the page and fan out upwards to towards the right as the numbers increase. Basically just like a line graph.
I'd like to stay away from libraries if at all possible.
How would I approach this? Thank you all.
let interval = setInterval(makeDivs, 5);
function makeDivs(){
let cont = checkHeight();
if(cont){
let div = document.createElement('div');
let randNum = Math.random() * 100;
if(randNum < 20) { div.classList.add('blue') }
if(randNum >= 20 && randNum < 40) { div.classList.add('green') }
if(randNum >= 40 && randNum < 60) { div.classList.add('yellow') }
if(randNum >= 60 && randNum < 80) { div.classList.add('orange') }
if(randNum >= 80 && randNum < 101) { div.classList.add('red') }
div.textContent = randNum.toFixed(2);
document.querySelector('body').appendChild(div);
} else {
alert('done');
clearInterval(interval);
sortDivs(); // Begin sorting divs
}
}
function checkHeight(){
let w = window.innerHeight;
let b = document.querySelector('body').offsetHeight;
if(b < w) {
return true;
} else {
return false;
}
}
function sortDivs(){
document.querySelector("body div:last-child").remove();
alert('sorting now...')
}
* { box-sizing: border-box;}
body { width: 100vw; margin: 0; padding: 0; display: flex; flex-wrap: wrap; align-items: end;}
body div { width: calc(10% + 1px); text-align: center; border: 1px solid #ddd; margin: -1px 0 0 -1px; padding: 10px;}
body div.blue { background: aqua; }
body div.green { background: green; }
body div.yellow { background: yellow; }
body div.orange { background: orange; }
body div.red { background: red; }
UPDATE!!!
So I have this so far based on the feed back down below. The problem now is the sorting is only happening laterally and not on an angle (spreading right and to the top).
let interval = setInterval(makeDivs, 10);
function makeDivs(){
let cont = checkHeight();
if(cont){
let div = document.createElement('div');
let randNum = Math.random() * 100;
if(randNum < 20) { div.classList.add('blue') }
if(randNum >= 20 && randNum < 40) { div.classList.add('green') }
if(randNum >= 40 && randNum < 60) { div.classList.add('yellow') }
if(randNum >= 60 && randNum < 80) { div.classList.add('orange') }
if(randNum >= 80 && randNum < 101) { div.classList.add('red') }
div.textContent = randNum.toFixed(2);
document.querySelector('.outPut').appendChild(div);
} else {
clearInterval(interval);
document.querySelector(".outPut div:last-child").remove();
compileArrays(); // Begin sorting divs
}
}
function checkHeight(){
let w = window.innerHeight;
let b = document.querySelector('.outPut').offsetHeight;
if(b < w) {
return true;
} else {
return false;
}
}
function compileArrays(){
let divs = document.querySelectorAll('.outPut div');
let bArr = [], gArr = [], yArr = [], oArr = [], rArr = [];
divs.forEach( (d) => {
if( d.classList.contains('blue') ){ bArr.push(d) }
if( d.classList.contains('green') ){ gArr.push(d) }
if( d.classList.contains('yellow') ){ yArr.push(d) }
if( d.classList.contains('orange') ){ oArr.push(d) }
if( d.classList.contains('red') ){ rArr.push(d) }
});
let finalArr = sortArray(bArr).concat(sortArray(gArr)).concat(sortArray(yArr)).concat(sortArray(oArr)).concat(sortArray(rArr));
newDom(finalArr);
}
function sortArray(arr){
let newArr = arr;
newArr.sort( (a, b) => {
return a.innerText - b.innerText;
});
return newArr;
}
function newDom(arr){
let b = document.querySelector('.outPut');
b.innerHTML = '';
arr.reverse();
arr.forEach((a) => {
b.appendChild(a);
});
}
* { box-sizing: border-box;}
body { width: 100vw; height: 100vh; margin: 0; padding: 0; display: flex; align-items: flex-end;}
body .outPut { flex: 1; display: flex; flex-wrap: wrap; flex-direction:row-reverse; }
body .outPut div { width: calc(10% + 1px); text-align: center; border: 1px solid #ddd; margin: -1px 0 0 -1px; padding: 10px;}
body .outPut div.blue { background: aqua; }
body .outPut div.green { background: #44df15; }
body .outPut div.yellow { background: yellow; }
body .outPut div.orange { background: orange; }
body .outPut div.red { background: red; }
<div class="outPut"></div>
Supposed you already have a mechanism to organise such DIVs in a grid as shown, the following should give you what you are looking for:
var items = divList.filter((div) => div.nodeType == 1); // get rid of the whitespace text nodes
items.sort(function(a, b) {
return a.innerHTML == b.innerHTML
? 0
: (a.innerHTML > b.innerHTML ? 1 : -1);
});
Then, place them back in the DOM as needed, example:
for (i = 0; i < items.length; ++i) {
divList.appendChild(items[i]);
}
This worked with the first code example!!!
try this sortDivs function:
function sortDivs() {
document.querySelector("body div:last-child").remove();
alert('sorting now...')
let toSort = document.getElementsByTagName("div")
toSort = Array.prototype.slice.call(toSort, 0)
toSort.sort((a, b) => {
let aord = parseFloat(a.textContent);
let bord = parseFloat(b.textContent);
return bord - aord;
})
document.body.innerHTML = ""
for(var i = 0, l = toSort.length; i < l; i++) {
document.querySelector('body').appendChild(toSort[i]);
}
}
and in the css file set flex-wrap to wrap-reverse. Hope I could help :)
PS: please, implement some else if instead of doing only if
Here is a small fiddle with my sample code demonstrating a simple solution in pure JavaScript and absolute CSS positioning for what you are trying to achieve. Link
As some pointed out already, there might be a library, that already provides a better and complete solution for this - I did not research if it is so.
Code:
file.js
var container = document.getElementById("container")
var results = [1,2,3,4,5,6,7,8]
//you can pre-calculate the order of the distances
//here already orderdered array [distanec][X-axis][Y-axis]
var distances =[[0,0,0],
[1,1,0],
[1,0,1],
[1.414, 1,1],
[2,0,2],
[2,2,0],
[2.234, 2,1],
[2.234, 1,2]]
for (i = 0; i < results.length; i++){
var newDiv = document.createElement("div")
newDiv.className = "result"
newDiv.innerHTML = results[i]
newDiv.style.left = distances[i][1]*20 + "px"
newDiv.style.bottom = distances[i][2]*20 + "px"
container.appendChild(newDiv)
}
function setColor(element){
// set class based on value - you already have this part
}
style.css
#container {
border: 4px;
border-color: red;
border-style: solid;
height: 200px;
width: 200px;
position: relative;
}
.result{
border: 2px;
width: 20px;
height: 20px;
position: absolute;
border-color: blue;
border-style: solid;
text-align: center;
}
site.html
<div id="container">
</div>
Output:
I have a yellow box in a grid. When click button 'UP' the yellow box is going one box UP. How can I stop the yellow box when it arrives at the edge? I do not want it to go out of the grid.
let moveCounter = 0;
var grid = document.getElementById("grid-box");
for (var i = 1; i <= 100; i++) {
var square = document.createElement("div");
square.className = 'square';
square.id = 'square' + i;
grid.appendChild(square);
}
var playerTwo = [];
while (playerTwo.length < 1) {
var randomIndex = parseInt(99 * Math.random());
if (playerTwo.indexOf(randomIndex) === -1) {
playerTwo.push(randomIndex);
var drawPtwo = document.getElementById('square' + randomIndex);
$(drawPtwo).addClass("p-1")
}
};
$('#button_up').on('click', function() {
moveCounter += 1;
$pOne = $('.p-1')
var id = $pOne.attr('id')
var idNumber = +id.slice(6);
var idMove = idNumber - 10
var idUpMove = 'square' + idMove;
$pOne.removeClass('p-1');
$('#' + idUpMove).addClass('p-1');
});
#grid-box {
width: 400px;
height: 400px;
margin: 0 auto;
font-size: 0;
position: relative;
}
#grid-box>div.square {
font-size: 1rem;
vertical-align: top;
display: inline-block;
width: 10%;
height: 10%;
box-sizing: border-box;
border: 1px solid #000;
}
.p-1{
background-color: yellow;
}
<div id="grid-box"></div>
<div class="move">
<button id="button_up">UP</button>
<br>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
I am new to Javascript / jQuery. Any help will be much appreciated ! Thank you
let moveCounter = 0;
var grid = document.getElementById("grid-box");
for (var i = 1; i <= 100; i++) {
var square = document.createElement("div");
square.className = 'square';
square.id = 'square' + i;
grid.appendChild(square);
}
var playerTwo = [];
while (playerTwo.length < 1) {
var randomIndex = parseInt(99 * Math.random());
if (playerTwo.indexOf(randomIndex) === -1) {
playerTwo.push(randomIndex);
var drawPtwo = document.getElementById('square' + randomIndex);
$(drawPtwo).addClass("p-1")
}
};
$('#button_up').on('click', function() {
moveCounter += 1;
$pOne = $('.p-1')
var id = $pOne.attr('id')
var idNumber = +id.slice(6);
var idMove = idNumber - 10;
if(idMove < 0){idMove +=10;}
var idUpMove = 'square' + idMove;
$pOne.removeClass('p-1');
$('#' + idUpMove).addClass('p-1');
});
#grid-box {
width: 400px;
height: 400px;
margin: 0 auto;
font-size: 0;
position: relative;
}
#grid-box>div.square {
font-size: 1rem;
vertical-align: top;
display: inline-block;
width: 10%;
height: 10%;
box-sizing: border-box;
border: 1px solid #000;
}
.p-1{
background-color: yellow;
}
<div id="grid-box"></div>
<div class="move">
<button id="button_up">UP</button>
<br>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
Here I have added the condition which restrict box to out of the grid
if(idMove < 0){idMove +=10;}
if movable position is in the minus then it again initialise it existing position.
You can add a check to stop it from moving out of the squares
var idMove = idNumber - 10
if(idMove > 0){
// do all the moving stuffs
}
$('#button_up').on('click', function() {
moveCounter += 1;
$pOne = $('.p-1')
var id = $pOne.attr('id')
var idNumber = +id.slice(6);
var idMove = idNumber - 10;
if(idMove > 0) {
var idUpMove = 'square' + idMove;
$pOne.removeClass('p-1');
$('#' + idUpMove).addClass('p-1');
}
});
You can use an if statement to check idMove > 0) If it is, then you can move your square, if it isn't then you shouldn't move your square. it will be undefined, and so you can run your code only when pOne's id is not undefined.
See example below:
let moveCounter = 0;
var grid = document.getElementById("grid-box");
for (var i = 1; i <= 100; i++) {
var square = document.createElement("div");
square.className = 'square';
square.id = 'square' + i;
grid.appendChild(square);
}
var playerTwo = [];
while (playerTwo.length < 1) {
var randomIndex = parseInt(99 * Math.random());
if (playerTwo.indexOf(randomIndex) === -1) {
playerTwo.push(randomIndex);
var drawPtwo = document.getElementById('square' + randomIndex);
$(drawPtwo).addClass("p-1")
}
};
$('#button_up').on('click', function() {
moveCounter += 1;
$pOne = $('.p-1')
var id = $pOne.attr('id')
var idNumber = +id.slice(6);
var idMove = idNumber - 10
if (idMove > 0) {
var idUpMove = 'square' + idMove;
$pOne.removeClass('p-1');
$('#' + idUpMove).addClass('p-1');
}
});
#grid-box {
width: 400px;
height: 400px;
margin: 0 auto;
font-size: 0;
position: relative;
}
#grid-box>div.square {
font-size: 1rem;
vertical-align: top;
display: inline-block;
width: 10%;
height: 10%;
box-sizing: border-box;
border: 1px solid #000;
}
.p-1 {
background-color: yellow;
}
<div id="grid-box">
</div>
<div class="move">
<button id="button_up">UP</button><br>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
I've made a JavaScript dropdown menu. Everything works fine, except the background image. I have the image set to change when the dropdown menu is expanded, which also works fine.
The issue is with the headers. Unless the header is set to display inline-block or inline, the menu won't expand. When set to inline-block or inline everything expands when you click on the box. But if you click on the header itself, it adds the padding and border around the header and ads in the background image from the div. How do you prevent this from happening?
<div class="panel">
<div class="collapse"><h2>Features</h2></div>
<div class="elements">
text<br>text<br>text
</div>
</div>
<style>
h2 {/*display: inline-block;*/
/*display: inline;*/
margin: 0px;
padding: 0px 0px 0px 0px;
color: #ffffff;
text-align: center;
text-transform: uppercase;}
.expand,
.collapse {cursor: pointer;
background-position: center right;
background-repeat: no-repeat;
background-color: #000033;
border: 2px solid #990044;
color: #ffffff;
padding: 10px 0px;
text-align: center;}
.collapse {background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAABHUlEQVRIS+3USw6CMBAA0BYNutOjcAQ9iXHjhoXhBt4AEmwwbvQm6g04ii4hCLaNNRUp/dDgQllRPvMy05lC0PMFe/bAH7Re8R8qaYySTZGPoyBYXm3WMQwP04Gbhfd8FJDYtKTxNjkCCBf4Ni3y0dwWSrChm51wXI/FhjHaRXix5rKygtYwGr4C1QUitPdKUJ7xemILbcJw7JsDnBktqU20DfP9VfoaCxuoDCPJvc1hF1QF+wBNy6uKNYK6qA4mBFVRXawVlKHkPTfUbKJo65NuFJ1W0sNb1EjPgOQEUcakGbJIApRPQpoZ+1iaoQKqjCln2IJqYdpgrZGArEGaGke5pPzPZE/Juq0bjbtU9KPpc6MMTTGjPeyCfQV8AK4c2lwJRjQ3AAAAAElFTkSuQmCC);}
.expand {background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAABD0lEQVRIS+3Wyw2CQBAG4F1Q8KadSAnYgR0YL164SCWYKDHxonZgB1ICdiI3QR6yCgSVXXaAkGDkwgXyZf6dYcCo5Qu37KFugIaxH5FkdH1+hSYErpBgPck9E8j35AkUBYE5TEkqs6EoN1iApWmCUC6QgYHRUpCCOYk0zDUNV6VMkIYJSFAJFKLQim8glAqyME1b2AQ0zZ0CRQtBHiyNEop+gRCsCvoGVsGgaAbWwSDoE2wC40VxkxgPiteb7QFhPMsNsEPmLG196DZgolF0fFXYd614M47jhxvBCtEIXfy7rGZnKEq3k4jEZd3KPhMhcxqgYBV4gylZZaXf0qqR0t77g00n2pG/tjpl/37TPACe/d8VUJ3+EgAAAABJRU5ErkJggg==);}
.elements {background-color: #ccd9ff;
overflow: hidden;}
</style>
<script>
function aaManageEvent (eventObj, event, eventHandler) {
if (eventObj.addEventListener) {eventObj.addEventListener (event, eventHandler, false);}
else if (eventObj.attachEvent) {event = "on" + event; eventObj.attachEvent (event, eventHandler);}
}
window.onload = function () {
var divs = document.getElementsByTagName ("div");
for (var i = 0; i < divs.length; i++) {
if (divs[i].className == "collapse") {
aaManageEvent (divs [i], "click", spring.expandOrCollapse);
}
else if (divs[i].className == "elements") {
var height = divs [i].offsetHeight;
divs [i] .height = height;
if (divs [i] .id == "") divs [i].id = "div" + i;
divs [i].style.height = "0";
}
}
}
var spring = {
// adjust height
adjustItem : function (val, newItem) {
document.getElementById (newItem).style.height = val + "px";
},
// check if expand or collapse
expandOrCollapse : function (evnt) {
evnt = evnt ? evnt : window.event;
var target = evnt.target ? evnt.target : evnt.srcElement;
if (target.className == "collapse") spring.expand (target);
else spring.collapse (target);
},
// Expand Panel
expand : function (target) {
target.className = 'expand';
var children = target.parentNode.childNodes, panel;
for (var i = 0; i < children.length; i++) {
if (children [i].className == "elements") {
panel = children [i]; break;
}
}
var height = panel.height, incr = height / 20;
for (var i=0; i < 20; i++) {
var val = (i + 1) * incr;
var func = "spring.adjustItem (" + val + ", '" + panel.id + "')";
setTimeout (func, (i + 1) * 30);
}
},
// Collapse Panel
collapse : function (target) {
target.className = "collapse";
var children = target.parentNode.childNodes, panel;
for (var i = 0; i < children.length; i++) {
if (children [i].className == "elements") {
panel = children [i]; break;
}
}
var height = panel.height, decr = height / 20;
for (var i = 0; i < 20; i++) {
var val = height - (decr * (i + 1));;
var func = "spring.adjustItem (" + val + ", '" + panel.id + "')";
setTimeout (func, (i + 1) * 30);
}
}
};
</script>
When I click on the div, the dropdown works. But when I click on the header, I see an error in the browser console.
I think because when clicking on the <h2>Features</h2> element, the click event bubbles up to the <div class="collapse">, making the var target in this line not the <div class="collapse"> but the <h2>:
var target = evnt.target ? evnt.target : evnt.srcElement;
A possible solution to fix this is for example to add an id to this line:
<div id="header" class="collapse"><h2>Features</h2></div>
Then you can directly get that div by id and change the classname.
I've adjusted your expandOrCollapse function to make it toggle based on the classname from the div with id="header".
For example:
function aaManageEvent (eventObj, event, eventHandler) {
if (eventObj.addEventListener) {eventObj.addEventListener (event, eventHandler, false);}
else if (eventObj.attachEvent) {event = "on" + event; eventObj.attachEvent (event, eventHandler);}
}
window.onload = function () {
var divs = document.getElementsByTagName ("div");
for (var i = 0; i < divs.length; i++) {
if (divs[i].className == "collapse") {
aaManageEvent (divs [i], "click", spring.expandOrCollapse);
}
else if (divs[i].className == "elements") {
var height = divs [i].offsetHeight;
divs [i] .height = height;
if (divs [i] .id == "") divs [i].id = "div" + i;
divs [i].style.height = "0";
}
}
}
var spring = {
// adjust height
adjustItem : function (val, newItem) {
document.getElementById (newItem).style.height = val + "px";
},
// check if expand or collapse
expandOrCollapse : function (evnt) {
var header = document.getElementById('header');
if (header.className === "collapse") {
spring.expand(header);
} else {
spring.collapse(header);
}
},
// Expand Panel
expand : function (target) {
target.className = 'expand';
var children = target.parentNode.childNodes, panel;
for (var i = 0; i < children.length; i++) {
if (children [i].className == "elements") {
panel = children [i]; break;
}
}
var height = panel.height, incr = height / 20;
for (var i=0; i < 20; i++) {
var val = (i + 1) * incr;
var func = "spring.adjustItem (" + val + ", '" + panel.id + "')";
setTimeout (func, (i + 1) * 30);
}
},
// Collapse Panel
collapse : function (target) {
target.className = "collapse";
var children = target.parentNode.childNodes, panel;
for (var i = 0; i < children.length; i++) {
if (children [i].className == "elements") {
panel = children [i]; break;
}
}
var height = panel.height, decr = height / 20;
for (var i = 0; i < 20; i++) {
var val = height - (decr * (i + 1));;
var func = "spring.adjustItem (" + val + ", '" + panel.id + "')";
setTimeout (func, (i + 1) * 30);
}
}
};
h2 {/*display: inline-block;*/
/*display: inline;*/
margin: 0px;
padding: 0px 0px 0px 0px;
color: #ffffff;
text-align: center;
text-transform: uppercase;}
.expand,
.collapse {cursor: pointer;
background-position: center right;
background-repeat: no-repeat;
background-color: #000033;
border: 2px solid #990044;
color: #ffffff;
padding: 10px 0px;
text-align: center;}
.collapse {background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAABHUlEQVRIS+3USw6CMBAA0BYNutOjcAQ9iXHjhoXhBt4AEmwwbvQm6g04ii4hCLaNNRUp/dDgQllRPvMy05lC0PMFe/bAH7Re8R8qaYySTZGPoyBYXm3WMQwP04Gbhfd8FJDYtKTxNjkCCBf4Ni3y0dwWSrChm51wXI/FhjHaRXix5rKygtYwGr4C1QUitPdKUJ7xemILbcJw7JsDnBktqU20DfP9VfoaCxuoDCPJvc1hF1QF+wBNy6uKNYK6qA4mBFVRXawVlKHkPTfUbKJo65NuFJ1W0sNb1EjPgOQEUcakGbJIApRPQpoZ+1iaoQKqjCln2IJqYdpgrZGArEGaGke5pPzPZE/Juq0bjbtU9KPpc6MMTTGjPeyCfQV8AK4c2lwJRjQ3AAAAAElFTkSuQmCC);}
.expand {background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAABD0lEQVRIS+3Wyw2CQBAG4F1Q8KadSAnYgR0YL164SCWYKDHxonZgB1ICdiI3QR6yCgSVXXaAkGDkwgXyZf6dYcCo5Qu37KFugIaxH5FkdH1+hSYErpBgPck9E8j35AkUBYE5TEkqs6EoN1iApWmCUC6QgYHRUpCCOYk0zDUNV6VMkIYJSFAJFKLQim8glAqyME1b2AQ0zZ0CRQtBHiyNEop+gRCsCvoGVsGgaAbWwSDoE2wC40VxkxgPiteb7QFhPMsNsEPmLG196DZgolF0fFXYd614M47jhxvBCtEIXfy7rGZnKEq3k4jEZd3KPhMhcxqgYBV4gylZZaXf0qqR0t77g00n2pG/tjpl/37TPACe/d8VUJ3+EgAAAABJRU5ErkJggg==);}
.elements {background-color: #ccd9ff;
overflow: hidden;}
<div class="panel">
<div id="header" class="collapse"><h2>Features</h2></div>
<div class="elements">
text<br>text<br>text
</div>
</div>
Edit your declaration block of h2 as shown below. This will solve your problem.
h2 {
display: inline-block;
margin: 0px;
padding: 0px 0px 0px 0px;
color: #ffffff;
text-align: center;
text-transform: uppercase;
pointer-events: none; // this line solves your problem.
}
CSS property pointer-events let you control under what circumstances an element can become the target of mouse events. when you set it to none, the element will never be the target of mouse events. So, the click event passed on to its descendant elements (here the box).