For any element that is clicked in the stack, the target element should be assigned a higher z-index than all of those that come before it in the DOM. Then it should translateY() pixels up to the first element.
The desired result is for the target element to sit on top of the first element in the parent. Yet there is a bug where it makes it appear that some elements get a lower z-index . If this could be refactored better as well how could it be improved?
const divs = document.querySelectorAll("div");
divs.forEach((div) => {
div.addEventListener("click", (event) => {
let i = 0;
let previous_sibling = event.target.previousElementSibling;
while (previous_sibling != null) {
i++;
previous_sibling = previous_sibling.previousElementSibling;
}
event.target.style.cssText = `
position: relative;
z-index: ${i + 1};
transform: translateY(-${i * 45}px);
`;
})
})
.box {
width: 300px;
height: 45px;
/* border: 1px solid; */
margin: auto;
}
section {
margin-top: 50px;
}
div {
transition: transform .75s linear;
}
#box1 {
background: red;
}
#box2 {
background: blue;
}
#box3 {
background: green;
}
#box4 {
background: orange;
}
#box5 {
background: purple;
}
#box6 {
background: teal;
}
<section>
<div class="box" id="box1">1</div>
<div class="box" id="box2">2</div>
<div class="box" id="box3">3</div>
<div class="box" id="box4">4</div>
<div class="box" id="box5">5</div>
<div class="box" id="box6">6</div>
<div class="box" id="box1">7</div>
<div class="box" id="box2">8</div>
<div class="box" id="box3">9</div>
<div class="box" id="box4">10</div>
<div class="box" id="box5">11</div>
<div class="box" id="box6">12</div>
</section>
Currently the z-index gets calculated by the elements DOM position due to the use of previousElementSibling and the simple counter. Since translateY does not change the position of the elements in the DOM they can never be out of order. For example numer #3 can never be on top of #4 because it never is in the DOM. To change this behaviour you could simply store the highest z-index (here in the dataset of the section) and reuse / add to it.
const divs = document.querySelectorAll("div");
divs.forEach((div) => {
div.addEventListener("click", (event) => {
//REM: Get the highest z-index
let tCurrentZ = ~~div.parentNode.dataset.zindex;
let i = 0;
let previous_sibling = event.target.previousElementSibling;
while (previous_sibling != null) {
i++;
previous_sibling = previous_sibling.previousElementSibling;
}
event.target.style.cssText = `
position: relative;
z-index: ${tCurrentZ};
transform: translateY(-${i * 45}px);
`;
//REM: Increase the highest z-index
div.parentNode.dataset.zindex = tCurrentZ + 1
})
})
.box {
width: 300px;
height: 45px;
/* border: 1px solid; */
margin: auto;
}
section {
margin-top: 50px;
}
div {
transition: transform .75s linear;
}
#box1 {
background: red;
}
#box2 {
background: blue;
}
#box3 {
background: green;
}
#box4 {
background: orange;
}
#box5 {
background: purple;
}
#box6 {
background: teal;
}
<section>
<div class="box" id="box1">1</div>
<div class="box" id="box2">2</div>
<div class="box" id="box3">3</div>
<div class="box" id="box4">4</div>
<div class="box" id="box5">5</div>
<div class="box" id="box6">6</div>
<div class="box" id="box1">7</div>
<div class="box" id="box2">8</div>
<div class="box" id="box3">9</div>
<div class="box" id="box4">10</div>
<div class="box" id="box5">11</div>
<div class="box" id="box6">12</div>
</section>
Related
I'm struggling with infinite carousel below:
let $carousel = document.querySelector('.carousel');
let $ref_ribbon = document.querySelector('.carousel__ribbon');
let $ref_right = document.querySelector('.carousel__button--right');
let $ref_left = document.querySelector('.carousel__button--left');
let $ref_counter = 0;
let $direction;
const transfer = () => {
if ($direction === -1) {
$ref_ribbon.appendChild($ref_ribbon.firstElementChild);
} else if ($direction === 1) {
$ref_ribbon.prepend($ref_ribbon.lastElementChild);
}
$ref_ribbon.style.transition = "none";
$ref_ribbon.style.transform = "translateX(0px)";
setTimeout(function() {
$ref_ribbon.style.transition = "transform .7s ease-in-out";
})
}
const right_button = () => {
if ($direction === 1) {
$ref_ribbon.prepend($ref_ribbon.lastElementChild);
$direction = -1;
}
$direction = -1;
$carousel.style.justifyContent = 'flex-start';
$ref_ribbon.style.transform = `translateX(-${300}px)`;
}
const left_button = () => {
$ref_counter--;
if ($direction === -1) {
$ref_ribbon.appendChild($ref_ribbon.firstElementChild);
$direction = 1;
}
$direction = 1;
$carousel.style.justifyContent = 'flex-end';
$ref_ribbon.style.transform = `translateX(${300}px)`;
}
$ref_right.addEventListener('click', right_button);
$ref_left.addEventListener('click', left_button);
$ref_ribbon.addEventListener('transitionend', transfer)
.carousel {
display: flex;
margin: auto;
position: relative;
height: 200px;
width: 300px;
background-color: red;
justify-content: flex-start;
}
.carousel__button {
position: absolute;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
z-index: 100;
}
.carousel__button--left {
left: 0;
}
.carousel__button--right {
right: 0;
}
.carousel__ribbon {
display: flex;
flex-direction: row;
outline: 3px solid black;
height: 100%;
transition: transform .7s ease-in-out;
}
.carousel__pane {
display: flex;
background-color: skyblue;
height: 100%;
width: 300px;
flex-shrink: 0;
outline: 1px dashed navy;
}
.carousel__content {
text-align: center;
margin: auto;
}
.carousel__indicator {
display: flex;
gap: 10px;
left: 50%;
transform: translateX(-50%);
height: 30px;
position: absolute;
bottom: 0;
}
.carousel__circle {
height: 10px;
width: 10px;
background-color: gray;
border-radius: 50%;
cursor: pointer;
}
.carousel__circle--active {
background-color: black;
}
<div class="carousel">
<button class="carousel__button carousel__button--left"><</button>
<button class="carousel__button carousel__button--right">></button>
<div class="carousel__ribbon">
<div class="carousel__pane">
<p class="carousel__content">Pane 1</p>
</div>
<div class="carousel__pane">
<p class="carousel__content">Pane 2</p>
</div>
<div class="carousel__pane">
<p class="carousel__content">Pane 3</p>
</div>
<div class="carousel__pane">
<p class="carousel__content">Pane 4</p>
</div>
<div class="carousel__pane">
<p class="carousel__content">Pane 5</p>
</div>
</div>
<div class="carousel__indicator">
<div class="carousel__circle carousel__circle--active"></div>
<div class="carousel__circle"></div>
<div class="carousel__circle"></div>
<div class="carousel__circle"></div>
<div class="carousel__circle"></div>
</div>
</div>
I would like to connect indicator so when somebody click on proper circle then carousel will automatically slide to this particular panel. Also, I would like to set this circles that they will show which panel is currently active.
In addition, I would like to get such effect that carousel will jump to this particular panel immediately, ommiting other panels between.
So, if active one is first panel and I click fifth circle, then carousel will smoothly change panel like to the panel number two, but instead of number two I will see number five.
Sadly I always fail to get this effect. I would appriciate if somebody more experienced direct me how to deal with this problem.
This question already has answers here:
Is there a "previous sibling" selector?
(30 answers)
Closed 1 year ago.
I'll keep it short:
I'm kinda new to JS and CSS. So I essentially I'm trying to change the background of DIV1 whenever someone hovers on DIV2. This is what I have right now:
#DIV2:hover ~ #DIV1 {
background-color: black;
transition: all ease-in-out 0.5s;
}
div {
width: 100px;
height: 50px;
}
#DIV2 {
background-color: red;
}
<div id="DIV1"></div>
<div id="DIV2"></div>
The issue is this works when DIV2 comes before DIV1. I want it to work when DIV 1 comes before DIV2 because that's how my site is to be structured.
Now I understand that I can't really target backwards in DOM (at least that's how I understand css works.) So is there any way I can make this work? Would appreciate any response/feedback. Thanks!
Here is a little CSS-only trick by using ability set order of children in flex/grid container:
.container
{
display: grid;
}
.div1{order: 1}
.div2{order: 2}
.div3{order: 3}
.div4{order: 4}
.div5{order: 5}
.container > div
{
border: 1px solid black;
background-color: lightgreen;
}
.div5:hover
{
background-color: green;
}
.div1:hover
{
background-color: red;
}
.div5:hover ~ .div4,
.div4:hover ~ .div3,
.div3:hover ~ .div2,
.div2:hover ~ .div1
{
background-color: pink;
}
<div class="container">
<div class="div5">DIV5</div>
<div class="div4">DIV4</div>
<div class="div3">DIV3</div>
<div class="div2">DIV2</div>
<div class="div1">DIV1</div>
</div>
You can just set the events with Javascript with the callback mouseover for hover and mouseout for the action when mouse not hovering.
document.querySelector('#DIV2').addEventListener('mouseover', function() {
var d1 = document.querySelector('#DIV1');
d1.style.backgroundColor = "black";
});
document.querySelector('#DIV2').addEventListener('mouseout', function() {
var d1 = document.querySelector('#DIV1');
d1.style.backgroundColor = "white";
});
div {
text-align: center;
width: 500px;
height: 50px;
}
<div id="DIV1">1</div>
<div id="DIV2">2</div>
If you need to do this on multiple pairs of divs (using class of course) that do not have a common parent element - without changing your HTML
document.querySelectorAll('.DIV1').forEach(d1 => {
const d2 = d1.parentElement.querySelector('.DIV1 ~ .DIV2');
if (d2) {
d2.addEventListener('mouseover', () => d1.classList.add('phover'));
d2.addEventListener('mouseout', () => d1.classList.remove('phover'));
}
});
.phover {
background-color: black;
color: white;
}
.DIV2 {
cursor: default;
}
<div>
<div class="DIV1">ONE</div>
<div class="DIV2">TWO</div>
</div>
<div>
<div class="DIV1">ONE</div>
<div class="DIV2">TWO</div>
</div>
<div>
<div class="DIV1">ONE</div>
<div class="DIV2">TWO</div>
</div>
<div>
If you want to do it on multiple pairs of divs with the same parent element - without changing your HTML
document.querySelectorAll('.DIV1').forEach(d1 => {
for (let d2 = d1.nextElementSibling; d2; d2 = d2.nextElementSibling) {
if (d2.classList.contains('DIV2')) {
d2.addEventListener('mouseover', () => d1.classList.add('phover'));
d2.addEventListener('mouseout', () => d1.classList.remove('phover'));
break;
}
}
});
.phover {
background-color: black;
color:white;
}
.DIV2 {
cursor:default;
}
<div class="DIV1">ONE</div>
<div class="DIV2">TWO</div>
<div class="DIV1">ONE</div>
<div class="DIV2">TWO</div>
<div class="DIV1">ONE</div>
<div class="DIV2">TWO</div>
Multiple pairs of DIVS, change the HTML
document.querySelectorAll('.DIV2').forEach(d2 => {
const d1 = document.querySelector(d2.dataset.for);
if (d1) {
d2.addEventListener('mouseover', () => d1.classList.add('phover'));
d2.addEventListener('mouseout', () => d1.classList.remove('phover'));
}
});
.phover {
background-color: black;
color: white;
}
.DIV2 {
cursor: default;
}
<div id="d1" class="DIV1">ONE</div>
<div class="DIV2" data-for="#d1">TWO</div>
<div id="d2" class="DIV1">ONE</div>
<div class="DIV2" data-for="#d2">TWO</div>
<div id="d3" class="DIV1">ONE</div>
<div class="DIV2" data-for="#d3">TWO</div>
For the simple case where DIV1 and DIV2 are within the same container you can set the color on hover of the container, putting back DIV1's color if DIV1 was the part that was being hovered.
Of course, if there are other elements DIV3 etc you'd have to ensure their colors were set back on hover in the same way so it's a bit of a messy solution if you have a complex set-up.
.container {
display: inline-block;
}
#DIV1,
#DIV2 {
width: 40vmin;
height: 40vmin;
}
#DIV2 {
background-color: cyan;
}
.container:hover #DIV1 {
background-color: black;
transition: all ease-in-out 0.5s;
}
.container #DIV1,
.container #DIV1:hover {
background-color: magenta;
}
<div class="container">
<div id="DIV1">DIV1</div>
<div id="DIV2">DIV2</div>
</div>
I am 95% of the way done with what I would like to have this webpage do. On this page I am looking to have my search bar filter the boxed options and fill them towards the top left. Right now, the boxes filter correctly and fill upwards (not quite sure why they filter upwards), but they don't fill upwards AND to the left. Any help would be greatly appreciated!
Note: The snippet runs, but there is no container for the boxes where as squarespace naturally makes these.
function search_topics() {
let input = document.getElementById('searchbar').value
input = input.toLowerCase();
let x = document.getElementsByClassName('text');
let y = document.getElementsByClassName('overlay-image _b1')
for (i = 0; i < x.length; i++) {
if (!x[i].innerHTML.toLowerCase().includes(input)) {
y[i].style.display = "none";
} else {
y[i].style.display = "table-cell";
}
}
/* Main container */
.overlay-image {
position: relative;
width: 100%;
}
/* Original image */
.overlay-image .image {
display: block;
width: 100%;
height: auto;
}
/* Original text overlay */
.overlay-image .text {
color: #fff;
font-size: 2.0em;
text-shadow: 2px 2px 2px #000;
text-align: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
}
/* Text on hover formatting */
.overlay-image .text_2 {
color: #fff;
font-size: 0.75em;
line-height: 1.5em;
text-align: center;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
}
/* New overlay on hover */
.overlay-image .hover {
position: absolute;
top: 0;
height: 100%;
width: 100%;
opacity: 0;
transition: 0.5s ease;
background-color: rgba(0, 0, 0, 0.5);
}
/* New overlay appearance on hover */
.overlay-image:hover .hover {
opacity: 1;
}
.overlay-image .normal {
transition: 0.5s ease;
}
.overlay-image:hover .normal {
opacity: 0;
}
#searchbar{
margin-left: 15%;
padding:15px;
border-radius: 10px;
width: 70%;
}
<body>
<input id="searchbar" onkeyup="search_topics()" type="text"
name="search" placeholder="Search topics....">
</body>
<div class=" overlay-image _b1 ">
<a href="url for block">
<img class=" image _b2 " src="https://i.redd.it/m3u40szpez231.jpg" />
<div class=" normal _b4 ">
<div class="text">Title of a block</div>
</div>
<div class=" hover _b3 ">
<div class=" text_2 ">Test that appears when block is hovered</div>
</div>
</a>
</div>
<div class=" overlay-image _b1 ">
<a href="url for block">
<img class=" image _b2 " src="https://i.redd.it/m3u40szpez231.jpg" />
<div class=" normal _b4 ">
<div class="text">Title of a block 2</div>
</div>
<div class=" hover _b3 ">
<div class=" text_2 ">Test that appears when block is hovered</div>
</div>
</a>
</div>
Your items move upward because the rows above are emptied of displayed content, allowing them to collapse. They don't move to the left because there are still table cells there. The problem you're having is that you're using a table to do work that tables were not designed to do. Css has a tool to do what you want called flexbox. Here's an example: https://codesandbox.io/s/tender-sky-9yyxf
<body>
<input id="search" type="text" placeholder="search"></input>
<div id="options">
<div value="coriander">Coriander</div>
<div value="anise">Anise</div>
<div value="lavender">Lavender</div>
<div value="fennel">Fennel</div>
<div value="ginger">Ginger</div>
<div value="sage">Sage</div>
<div value="cinnamon">Cinnamon</div>
</div>
<script src="src/index.js"></script>
</body>
import "./styles.css";
const search = document.getElementById("search");
if (search) {
search.addEventListener("change", event => {
if (event.target.value) {
// Normalize the search term.
const value = event.target.value.toLowerCase();
console.log(value);
// Hide all non matching elements.
document
.querySelectorAll(`#options :not([value*=${value})`)
.forEach(item => {
item.classList.add("hidden");
});
// Show all matching elements.
document.querySelectorAll(`#options [value*=${value}]`).forEach(item => {
item.classList.remove("hidden");
});
} else {
// If there are no search terms, show all elements.
document.querySelectorAll(`#options div`).forEach(item => {
item.classList.remove("hidden");
});
}
});
}
#options {
/* Set the options to fit three items in a row, and wrap them. */
display: flex;
flex-direction: row;
/* Setting max-width instead of width will allow your list to automatically adjust to smaller screen sizes. */
max-width: 70em;
flex-wrap: wrap;
}
#options div {
/* Set the items to a fixed width */
width: 20em;
border: 1px solid red;
margin: 0.5em;
text-align: center;
}
#options div.hidden {
/* Add a style to hide items when they don't apply to the search. */
display: none;
}
I am trying to create a box that can expand and collapse with a simple slide out animation. If you run the example below, the idea is that it starts with one red line and when you click the button it separates into two read lines and gently expands to reveal the content like pulling a draw out of a table.
I've tried both transform, animation, relative: positioning with top, and i'm unable to get the desired effect.
The containing box should expand in size
function expandContract() {
const el = document.getElementById("expand-contract")
el.classList.toggle('expanded')
el.classList.toggle('collapsed')
}
#container {
border: 1px solid black;
padding: 15px;
}
#top-section {
border-bottom: 1px solid red;
}
#expand-contract {
border-bottom: 1px solid red;
}
.expand-contract {
transform: translateY(-100%)
overflow: hidden;
}
#keyframes slide-in {
100% {
transform: translateY(0%)
}
}
.expanded {
background-color: green;
animation-name: slide-in;
animation-duration: 1s;
}
.collapsed {
background-color: red;
transform: translateY(-100%)
}
<div id="container">
<div id="top-section">
This is always displayed
</div>
<div id="expand-contract" class="expanded">
This section expands and contracts
<table>
<tr><td>test1</td></tr>
<tr><td>test2</td></tr>
<tr><td>test3</td></tr>
<tr><td>test4</td></tr>
</table>
</div>
<div id="bottom-section">
This section is always displayed
</div>
</div>
<button onclick="expandContract()">Expand/Contract</button>
You can achieve this using the CSS transition along with toggled styles. Initially you may think to transition the height (from 0 to initial so that it expands dynamically based on height) but unfortunately CSS transition doesn't properly handle this.
Instead, you can wrap it in a container of its own with overflow: hidden and then use a margin-top: -100% to hide it, and 0 to show it.
Here is your code with this modification:
function expandContract() {
const el = document.getElementById("expand-contract")
el.classList.toggle('expanded')
el.classList.toggle('collapsed')
}
#container {
border: 1px solid black;
padding: 15px;
}
#top-section {
border-bottom: 1px solid red;
}
#expand-container {
overflow: hidden;
}
#expand-contract {
border-bottom: 1px solid red;
margin-top: -100%;
transition: all 1s;
}
#expand-contract.expanded {
background-color: green;
margin-top: 0;
}
<div id="container">
<div id="top-section">
This is always displayed
</div>
<div id="expand-container">
<div id="expand-contract" class="expanded">
This section expands and contracts
<table>
<tr><td>test1</td></tr>
<tr><td>test2</td></tr>
<tr><td>test3</td></tr>
<tr><td>test4</td></tr>
</table>
</div>
</div>
<div id="bottom-section">
This section is always displayed
</div>
</div>
<button onclick="expandContract()">Expand/Contract</button>
hope to help you
HTML
<div class="container">
<div id="top-section">
This is always displayed
</div>
<div id="expand-container">
<div class="expanded" id="expand-contract">
<table>
<tr><td>test1</td></tr>
<tr><td>test2</td></tr>
<tr><td>test3</td></tr>
<tr><td>test4</td></tr>
</table>
</div>
</div>
</div>
<button class="header" onclick="expandContract()">Expand/Contract</button>
css
.container {
width:100%;
border:1px solid #d3d3d3;
}
.container div {
width:100%;
}
.header {
background-color:#d3d3d3;
padding: 2px;
cursor: pointer;
font-weight: bold;
}
.container .expanded {
display: none;
padding : 5px;
}
js
function expandContract() {
$header = $(".header");
$content = $("#expand-contract")
$content.slideToggle(500, function () {
$header.text(function () {
return $content.is(":visible") ? "Collapse" : "Expand";
});
});
};
see here enter code here
I’m having a little trouble with this template: basically, I’m trying to add functionality where if you click a box it will expand sliding the other ones off-screen, but instead sliding the div off-screen it’s disappearing completely.
Here is what I have so far: JSFiddle.
$(function() {
$(".box").click(function() {
var isopened = $(this).attr("isopen");
if (isopened == "true") {
$(this).css("position", "relative").css("width", $(this).attr("data-ow"));
$(this).attr("isopen", "false");
}
else {
$(this).attr("data-ow", $(this).css("width"));
$(this).css("position", "relative").css("width", "40%");
$(this).attr("isopen", "true");
}
});
});
* {
margin: 0;
padding: 0;
}
.container {
width: 100%;
max-width: 100%;
max-height: 600px;
overflow: hidden;
}
.box {
height: 600px;
display: block;
width: 13.33333333%;
border: 1px solid white;
background-color: black;
float: right;
position: relative;
}
.box:first-of-type {
width: 29.0%;
background-color: orange;
}
.box:last-of-type {
width: 29.0%;
background-color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
What I ultimately want is when one of the boxes is clicked it expands and instead of the entire div being hidden only the part which is off-screen is hidden:
I think you might like this flexbox solution as you can do what you want without usign any jQuery/JS. Pure CSS and HTML:
body {
background-color: black
}
#container {
display: flex;
width: 100%;
height: 50vh;
}
#container > div {
flex: 1;
min-width: 0;
transition:min-width 0.2s ease;
outline:0;
}
#container > div:focus {
min-width: 50vw;
}
<div id="container">
<div tabindex="0" style="background-color:blue"></div>
<div tabindex="0" style="background-color:orange"></div>
<div tabindex="0" style="background-color:green"></div>
<div tabindex="0" style="background-color:white"></div>
<div tabindex="0" style="background-color:blue"></div>
</div>
I used tabindex to give me the ability to use the :focus selector.