I'm trying to create a collapsible sidebar on the left.
I want to set it up real simple: 2 columns which contains of 2 flex boxes, and when a button is pushed: the left flex box increases in width and the tight flexbox just moves with. When the button is clicked again, the flexbox on the left decreases again in side, back to the first state where the menu cannot be seen.
My problem is that I don't know how a click event of a button can control the width size of the flexbox.
What I have now is this:
html
<div>
<button
onClick={handleViewMenu}??
style={{ height: "30px", width: "30px" }}>
</button>
</div>
<div className='container'>
<div className='container-left'>
Left
</div>
<div className='container-right'>
right
</div>
</div>
scss
.container { width: 100vw;
height: 100vh;
display: flex;
&-left {
flex-grow: 0;
flex-shrink: 0;
flex-basis: auto;
// flex: 0.3 0 auto;
// background-color: aqua;
} &-right {
flex: 1 0 auto;
}
}
I just don't know how to deal with the onClick event (where I put the ??. I work in React so I found different things like:
const [sideMenuOpen, setMenuOpen] = useState(false);
const handleViewMenu = () => {
setMenuOpen(!sideMenuOpen);
};
But it should be pretty easy to handle this I think, but I can't find a solution..
Here's a solution that doesn't need javascript by using the :has() pseudo class. Just set the width of the side bar when the checkbox is clicked and if you're using normal flexbox the right hand one will automatically shift to suit. See below. Any questions drop me a comment.
/* essential to add this as there's a default 8px margin */
body {
margin: 0;
}
/* this is also essential to avoid a world of width-based pain */
* {
box-sizing: border-box;
}
/* Just making things pretty here */
nav {
display: flex;
width: 100%;
gap: 1rem;
padding: 0.5rem 1rem;
background-color: purple;
color: white;
}
/*the menu button is basically a hidden check box, we use label to style it as a button */
.menubutton>input {
display: none;
}
/*these toggles the display of the menu button, it works because the label after the input element */
.menubutton>input:checked+label .not-active {
display: none;
}
.menubutton>input:not(:checked)+label .active {
display: none;
}
.container {
display: flex;
width: 100%;
}
.container-left {
background-color: plum;
height: 50vh;
padding: 0.5rem 0.5rem;
border: 1px solid transparent;
width: 3rem;
transition: width 300ms;
}
/* this is the bit that styles the width of the sidebar when the checkbox is checked. */
body:has(.menubutton > input:checked) .container-left {
width: 10rem;
}
/* just style the right box for visibility */
.container-right {
border: 1px solid lightgray;
height: 50vh;
flex-grow: 1;
padding: 0.5rem 0.5rem;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
<nav>
<div class='menubutton'><input type='checkbox' id='menubuttoninput'><label for='menubuttoninput'><i class="fa-solid fa-bars not-active"></i><i class="fa-solid fa-xmark active"></i></label></div>
This is a navbar!
</nav>
</div>
<div class='container'>
<div class='container-left'>
Left
</div>
<div class='container-right'>
right
</div>
</div>
If you do need a javascript solution then attach a listener to the checkbox input element and toggle the sidebar class to change the width as below:
window.onload = () => {
document.querySelector('.menubutton input').addEventListener('change', (e) => {
const sidebar = document.querySelector('.container-left');
if (e.target.checked) {
sidebar.classList.add('sidebar-active');
} else {
sidebar.classList.remove('sidebar-active');
}
});
}
/* essential to add this as there's a default 8px margin */
body {
margin: 0;
}
/* this is also essential to avoid a world of width-based pain */
* {
box-sizing: border-box;
}
/* Just making things pretty here */
nav {
display: flex;
width: 100%;
gap: 1rem;
padding: 0.5rem 1rem;
background-color: cornflowerblue;
color: white;
}
/*the menu button is basically a hidden check box, we use label to style it as a button */
.menubutton>input {
display: none;
}
/*these toggles the display of the menu button, it works because the label after the input element */
.menubutton>input:checked+label .not-active {
display: none;
}
.menubutton>input:not(:checked)+label .active {
display: none;
}
.container {
display: flex;
width: 100%;
}
.container-left {
background-color: lightblue;
height: 50vh;
padding: 0.5rem 0.5rem;
border: 1px solid transparent;
width: 3rem;
transition: width 300ms;
}
/* this is the bit that styles the width of the sidebar when the checkbox is checked. We just add this using javascript*/
.sidebar-active {
width: 10rem;
}
/* just style the right box for visibility */
.container-right {
border: 1px solid lightgray;
height: 50vh;
flex-grow: 1;
padding: 0.5rem 0.5rem;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
<nav>
<div class='menubutton'><input type='checkbox' id='menubuttoninput'><label for='menubuttoninput'><i class="fa-solid fa-bars not-active"></i><i class="fa-solid fa-xmark active"></i></label></div>
This is a navbar!
</nav>
</div>
<div class='container'>
<div class='container-left'>
Left
</div>
<div class='container-right'>
right
</div>
</div>
I want exactly like this website: https://www.petzl.com/INT/en
I have displayed background video, 3 texts, by hovering on them the images are changing + links to access them.
But by hovering the text, petzl.com website have activated different images on hover, sometimes different images are appearing on hover.
Can I achieve the different images by css or need to have js or anything? Also need to have span/div elements to appear in one line.
My website link: beta.edgerope.com
Please find code below:
Below is the code, I have added as HTML shortcode in the page and in the background video is displayed
<style>
.image{
height: 800px;
width: 100%;
display: grid;
place-content:center;
justify-content:middle;
color: white;
font-size:30px;
background-color: #;
background-position: center;
background-size: cover;
}
.training{
display:inline-block;
padding-top: 50px;
padding-right: 1500px;
padding-bottom: 50px;
padding-left: 1px;
}
.image>div {
display: inline-block;
width: 100px;
}
.image>div img {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
object-fit: cover;
opacity: 0;
z-index: 0;
display: inline-block;
}
.image>div span {
position: relative;
z-index: 1;
cursor: pointer;
display: inline-block;
}
.image>div span:hover+img {
opacity: 1;
display: inline-block;
}
.div{
dipslay
}
</style>
<div class="image">
<div class="services">
<span class="services" onclick="window.location=''">Services</span>
<img src="https://www.petzl.com/sfc/servlet.shepherd/version/download/0686800000D6sSCAAZ">
</div>
<div class="Training">
<span class="training" onclick="window.location='beta.edgerope.com/courses'">Training</span>
<img src="https://beta.edgerope.com/wp-content/uploads/2022/08/1-1-1536x864.jpg"> </div>
<div class="shop">
<span class="shop" onclick="window.location='beta.edgerope.com/shop'">Shop</span>
<img src="https://www.petzl.com/sfc/servlet.shepherd/version/download/0686800000D6sSCAAZ"> </div>
</div>
:
There are many ways to acomplish this, and you should be able to do this with pure css and html.
I've made an example that uses css pseudo elements in order to display the correct image when hovering the sections/links. Also changed up to use a tags instead of using onclick="window.location" as you did.
Here you can change/set the default image in the #hero selector
<style>
/* Styling for the default hero */
#hero {
height: 800px;
width: 100%;
display: flex;
color: black;
background-color: #000;
}
/* Wrapper for all the sections */
.hero-sections {
width: 100%;
height: 100%;
position: relative; /* Allows to change the z-index, and */
z-index: 1;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 2rem;
}
/* Container for the image */
.hero-image::after {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: -1; /* Allows the image to appear behind the text */
pointer-events: none; /* Prevent image to show when hovering the ::after element*/
background-image: var(
--bg-url
); /* Uses the images set by the html (css variable) */
opacity: 0; /* Hides the image when it's not active */
transition: opacity 0.5s; /* Adds a fade transition to the opacity*/
}
/* Styles when the the user hovers or has focus on the buttons/links */
.hero-image:is(:hover, :focus-within)::after {
opacity: 1; /* Change the opacity when the text is active/in hover */
}
/* Styles the buttons/links */
.hero-cta {
color: white;
text-decoration: none;
font-size: 2rem;
}
</style>
<div id="hero">
<div class="hero-sections">
<div
class="services hero-image"
style="
--bg-url: url(https://www.petzl.com/sfc/servlet.shepherd/version/download/0686800000D6sSCAAZ);
"
>
<a class="hero-cta" href="#">Services</a>
</div>
<div
class="training hero-image"
style="
--bg-url: url(https://beta.edgerope.com/wp-content/uploads/2022/08/1-1-1536x864.jpg);
"
>
<a class="hero-cta" href="beta.edgerope.com/courses">Training</a>
</div>
<div
class="shop hero-image"
style="
--bg-url: url(https://www.petzl.com/sfc/servlet.shepherd/version/download/0686800000D6sSCAAZ);
"
>
<a class="hero-cta" href="beta.edgerope.com/shop">Shop</a>
</div>
</div>
</div>
If you would like to go more advanced here I would recommend implementing some javascript handling mouseenter and mouseleave. As you said you used Wordpress you could also use the .hover() from jQuery.
I have two sections one over the other.
What I want to do is that when I click the button, the first section display: none with a small transition and the second one appears. I need to do that with vanilla JavaScript as I'm learning it.
My goal is to be able to create a login when the password is entered, the person is moved to the second section. (this is just for Front end, nothing backend).
Here is the code below:
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* Login Background */
#first{
position: absolute;
width: 100%;
height: 100vh;
background-color: #464159;
z-index: 1;
}
/* Login */
.login-container{
position: absolute;
top: 50%;
right: 50%;
transform: translate(50%, -100%);
display: flex;
align-items: center;
justify-content: center;
}
.logo{
justify-content: center;
margin-right: 20px;
}
.logo img{
width: 180px;
height: auto;
}
.user{
display: flex;
flex-direction: column;
}
.w8u{
color: white;
font-size: 15px;
margin: 0 0 10px 0;
}
.submit{
flex: 1;
margin: 30px 0 0 10px;
}
/* Menu */
#second{
position: absolute;
width: 100%;
height: 100vh;
background-color: orangered;
z-index: -1;
}
<body>
<section id="first">
<div class="login-container">
<!-- Logo User -->
<div class="logo">
<img src="w-logo.jpg" alt="User">
</div>
<!-- User Name -->
<div class="user">
<div class="w8u">
<h3>User</h3>
</div>
<div>
<input class="input" type="password" placeholder="Enter your password" required>
</div>
</div>
<!-- Submit Button -->
<button id="btn-submit" class="submit" type="submit" onclick="">GO!</button>
</section>
<section id="second">
</section>
<script src="app.js"></script>
</body>
Yes, you can change CSS of elements using JavaScript:
function login(){
document.getElementById("first").style.display="none";
document.getElementById("second").style.display="block";
}
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* Login Background */
#first{
position: absolute;
width: 100%;
height: 100vh;
background-color: #464159;
z-index: 1;
}
/* Login */
.login-container{
position: absolute;
top: 50%;
right: 50%;
transform: translate(50%, -100%);
display: flex;
align-items: center;
justify-content: center;
}
.logo{
justify-content: center;
margin-right: 20px;
}
.logo img{
width: 180px;
height: auto;
}
.user{
display: flex;
flex-direction: column;
}
.w8u{
color: white;
font-size: 15px;
margin: 0 0 10px 0;
}
.submit{
flex: 1;
margin: 30px 0 0 10px;
}
/* Menu */
#second{
position: absolute;
width: 100%;
height: 100vh;
background-color: orangered;
z-index: -1;
}
<body>
<section id="first">
<div class="login-container">
<!-- Logo User -->
<div class="logo">
<img src="w-logo.jpg" alt="User">
</div>
<!-- User Name -->
<div class="user">
<div class="w8u">
<h3>User</h3>
</div>
<div>
<input class="input" type="password" placeholder="Enter your password" required>
</div>
</div>
<!-- Submit Button -->
<button id="btn-submit" class="submit" type="submit" onclick="login()">GO!</button>
</section>
<section id="second">
Section 2
</section>
<script>
</script>
</body>
Short Answer:
You can solve your issue by using a common pattern of styling transitions and toggling classes in JavaScript.
/* Get Screen Container */
let context = document.querySelector("main.screen-container");
/* Helper Methods */
// Utility
const ele = context.querySelector.bind(context),
eles = context.querySelectorAll.bind(context);
// Action
const showScreen = inactiveScreen => {
const screens = eles(".screen");
screens.forEach(screen => screen.classList.remove("active-screen"));
inactiveScreen.classList.add("active-screen");
}
// Elements
const login_button = ele("#loginBtn"),
logout_button = ele("#logoutBtn"),
login_screen = ele("#login"),
loggedIn_screen = ele("#loggedIn");
// Add Event Listeners and Handlers
login_button.addEventListener("click", () => showScreen(loggedIn_screen));
logout_button.addEventListener("click", () => showScreen(login_screen));
.full-width-full-height {
width: 100%;
height: 100%;
}
.no-overflow {
overflow: hidden;
}
.inputs-container {
display: flex;
flex-direction: column;
justify-content: center;
height: 200px;
padding: 5px;
border: 5px black solid;
}
.inputs-container.inputs {
padding: 10px;
}
.screen#login {
display: flex;
justify-content: center;
}
.screen#loggedIn {
background: rgba(0,0,0,.5);
border: 5px solid rgba(0,0,0,.3);
text-align: center;
}
highlight {
color: rgb(0,100,0);
text-shadow: 3px 3px 10px rgb(0,200,0);
}
.screen-container {
height: 100%;
width: 100%;
overflow: hidden;
padding: 0px;
margin: 0px;
}
main section {
position: absolute;
height: 0px;
width: 0px;
opacity: 0;
transition: all .3s ease-in-out;
}
main section.active-screen {
height: 100%;
width: 100%;
opacity: 1;
}
<body class="full-width-full-height no-overflow">
<main class="screen-container full-width-full-height">
<section id="login" class="screen active-screen">
<div class="inputs-container">
<div class="inputs"><label>Username:</label><input type="text" id="username" placeholder="admin" /></div>
<div class="inputs"><label>Password:</label><input type="password" id="password" /></div>
<button id="loginBtn">login</button>
</div>
</section>
<section id="loggedIn" class="screen">
<h3>User Successfully <highlight>Logged In</highlight></h3>
<button id="logoutBtn">logout</button>
</section>
</main>
</body>
Explanation of Answer:
In your question you're asking for help in the following:
Using Sections as Different Screens in an Application
Change Screens on an Event such as click
Transition between Screens when the Screen Changes
Something like this isn't an atypical need and, in fact, just looking at the above you can see the resemblance between what you're looking for and common things like a carousel or tab navigation.
Since this is the case there is a pretty common pattern when designing markup based around these types of requirements and this pattern is used in frameworks like BootStrap, DataTables, JQuery, etc.
The Mark-Up Pattern:
Create a Container
Provide a class to define this type of container ( screen-container )
Create Visual Elements
In your case Section Elements dubbed Screens
Provide a class to define these specific elements
In your case something like screen
You will likely also want to provide an ID for each of these Visual Elements based on their Content.
In your case something like login and loggedIn
Create a class that will serve as the active Visual Element
In your case something like active-screen
Incorporating the above you end up with a basic outline:
<main class="screen-container">
<section id="login" class="screen active-screen"></section>
<section id="loggedIn" class="screen"></section>
</main>
Example:
html, body, main, .screen-container {
height: 100%;
width: 100%;
}
.screen-container
.screen {
padding: 3px;
background: rgba(24,24,24, .3);
border: 3px solid black;
width: 100px;
height: 100px;
}
.screen-container
.screen.active-screen {
border-color: green;
}
<main class="screen-container">
<section id="login" class="screen active-screen">login</section>
<section id="loggedIn" class="screen">logged in</section>
</main>
Styling:
In your initial question you say that you want to use the display property to hide and show your screens, however, you also state that you would like to have the screens transition from one to another.
Transitioning is definitely possible through the aptly named CSS transition property ( More info on MDN ).
The problem is that the display property is not able to be animated. If an element is configured to display: none; the page is immediately repainted with that element removed.
This means that you need to use a different property, and we typically would use opacity or visibility. Here are the differences between these three:
display: none;
immediately collapses the element
removes the element from view.
There's no transition allowed.
visibility: hidden;
Does not collapse the element
The space it occupied is blank.
removes the element from view
Transitions are allowed
The element will still pop out of sight.
opacity: 0;
Does not collapse the element
The space it occupied is blank.
removes the element from view
Transitions are allowed.
The element will fade until it is not visible.
Here is an example of the different way these properties affect the layout of the page:
const context = document.querySelector("#examples");
const ele = context.querySelector.bind(context),
hide = section => section.classList.toggle("hide"),
onClickHide = (btn, section) => btn.addEventListener("click", () => hide(section));
opacity = ele(".opacity"),
opacity_button = ele("#oBtn"),
visibility = ele(".visibility"),
visibility_button = ele("#vBtn"),
display = ele(".display"),
display_button = ele("#dBtn"),
toggle_button = ele("#tBtn");
onClickHide(opacity_button, opacity);
onClickHide(visibility_button, visibility);
onClickHide(display_button, display);
toggle_button
.addEventListener("click", function() {
hide(opacity);
hide(visibility);
hide(display);
});
html,
body,
#examples {
width: 100%;
height: 100%;
box-sizing: content-box;
margin: 0px;
padding: 0px;
}
#examples section {
display: flex;
flex-direction: column;
width: 100px;
height: 100px;
border: 5px solid black;
margin: 5px;
transition: all .5s ease-in-out;
}
#examples section.hide {
border-radius: 100px;
}
#examples section.opacity {
background-color: blue;
color: white;
}
#examples section.opacity.hide {
opacity: 0;
}
#examples section.visibility {
background-color: purple;
color: white;
}
#examples section.visibility.hide {
visibility: hidden;
}
#examples section.display {
display: block;
background-color: red;
color: white;
}
#examples section.display.hide {
color: black;
display: none;
}
<main id="examples">
<section class="opacity">opacity <button id="oBtn">hide</button></section>
<hr />
<section class="visibility">visibility <button id="vBtn">hide</button></section>
<hr />
<section class="display">display <button id="dBtn">hide</button></section>
<hr/>
<button id="tBtn">Toggle All</button>
</main>
Note: In the above there are actually two properties transitioning - opacity, visibility, or display - and border-radius. You should notice firstly how in the display example the border-radius change isn't seen at all, and secondly how the display example is the only one that collapses the element so that it no longer takes up space.
Applying Transitions:
By combining opacity: 0; with height: 0px; width: 0px; we can remove the element visually from the page while also removing any impact it has on other elements - meaning that it won't take up space and is transitionable.
setInterval(function() {
const screens = [
document.querySelector(".screen.active-screen"),
document.querySelector(".screen:not(.active-screen)")
];
screens[0].classList.toggle("active-screen");
screens[0].ontransitionend = () => {
screens[1].classList.toggle("active-screen");
screens[0].ontransitionend = undefined;
}
}, 1000)
html,
body,
main,
.screen-container {
height: 100%;
width: 100%;
}
.screen-container .screen {
transition: all .3s ease-in-out;
opacity: 0;
height: 0px;
width: 0px;
}
.screen-container .screen.active-screen {
background: rgba(24, 24, 24, .3);
border: 3px solid black;
padding: 3px;
border-color: green;
width: 100px;
height: 100px;
opacity: 1;
}
<main class="screen-container">
<section id="login" class="screen active-screen">login</section>
<section id="loggedIn" class="screen">logged in</section>
</main>
JavaScript
The final piece of the puzzle is the JavaScript mechanics of the Screens.
Any Programmer wants to make the switch as easy as possible, and this is done typically by providing a function that allows for quick reassignment of the class active-screen by removing it from the current active screen and applying it to the desired visual element.
One thing to take into account is that you want your queries for elements to be as specific as possible. Meaning that instead of document.querySelector you want to provide the smallest context of where to find your Visual Elements. a.e.
/* Get Screen Container */
let context = document.querySelector("main.screen-container");
/* Helper Methods */
// Utility
const ele = context.querySelector.bind(context),
eles = context.querySelectorAll.bind(context);
This prevents code collision where other code in your Application's JavaScript, Styling, or Mark-Up may utilize a screen or active-screen class that isn't relevant to what you're doing here.
Note: This is actually a problem in BootStrap currently. It searches for the active class in it's Tab architecture. This is such a generic class name that other libraries utilize it and it can cause a giant headache to get things to work properly. Writing explicit patching because you didn't think through your design fully is something I'd just as soon spare you from, so try to keep interactivity with other code in mind.
Lastly we write our function ( showScreen ) that allow for quick, easy switching between screens:
// Action
const showScreen = inactiveScreen => {
const screens = eles(".screen");
screens.forEach(screen => screen.classList.remove("active-screen"));
inactiveScreen.classList.add("active-screen");
}
And believe it or not, that's pretty much it!
All that's left to do is apply this functionality to your button click events and it works just as it should:
Result:
/* Get Screen Container */
let context = document.querySelector("main.screen-container");
/* Helper Methods */
// Utility
const ele = context.querySelector.bind(context),
eles = context.querySelectorAll.bind(context);
// Action
const showScreen = inactiveScreen => {
const screens = eles(".screen");
screens.forEach(screen => screen.classList.remove("active-screen"));
inactiveScreen.classList.add("active-screen");
}
// Elements
const login_button = ele("#loginBtn"),
logout_button = ele("#logoutBtn"),
login_screen = ele("#login"),
loggedIn_screen = ele("#loggedIn");
// Add Event Listeners and Handlers
login_button.addEventListener("click", () => showScreen(loggedIn_screen));
logout_button.addEventListener("click", () => showScreen(login_screen));
.full-width-full-height {
width: 100%;
height: 100%;
}
.no-overflow {
overflow: hidden;
}
.inputs-container {
display: flex;
flex-direction: column;
justify-content: center;
height: 200px;
padding: 5px;
border: 5px black solid;
}
.inputs-container.inputs {
padding: 10px;
}
.screen#login {
display: flex;
justify-content: center;
}
.screen#loggedIn {
background: rgba(0,0,0,.5);
border: 5px solid rgba(0,0,0,.3);
text-align: center;
}
highlight {
color: rgb(0,100,0);
text-shadow: 3px 3px 10px rgb(0,200,0);
}
.screen-container {
height: 100%;
width: 100%;
overflow: hidden;
padding: 0px;
margin: 0px;
}
main section {
position: absolute;
height: 0px;
width: 0px;
opacity: 0;
transition: all .3s ease-in-out;
}
main section.active-screen {
height: 100%;
width: 100%;
opacity: 1;
}
<body class="full-width-full-height no-overflow">
<main class="screen-container full-width-full-height">
<section id="login" class="screen active-screen">
<div class="inputs-container">
<div class="inputs"><label>Username:</label><input type="text" id="username" placeholder="admin" /></div>
<div class="inputs"><label>Password:</label><input type="password" id="password" /></div>
<button id="loginBtn">login</button>
</div>
</section>
<section id="loggedIn" class="screen">
<h3>User Successfully <highlight>Logged In</highlight></h3>
<button id="logoutBtn">logout</button>
</section>
</main>
</body>
Conclusion:
I hope this helps and gives you some insight into how this pattern is used! It's not a difficult thing to learn, but it's incredibly useful!
Good luck and Happy Coding!
In my project, I have a list of dropdown buttons which are aligned right using flex-end. I've created a very minimal version of the code that shows the issue:
var popper = new Popper(
$('#anchor')[0],
$('#popper')[0],
{
modifiers: {
preventOverflow: {
enabled: true,
boundariesElement: 'window'
}
}
}
);
body {
height: 2000px;
}
.parent {
display: flex;
justify-content: flex-end;
}
.anchor {
width: 40px;
height: 40px;
background-color: #333;
}
.popper {
width: 120px;
height: 80px;
background-color: #c23;
border: 2px solid #ae3;
}
ul {
list-style-type: none;
margin: 0 -6px;
}
li {
margin: 0 6px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/popper.js/dist/umd/popper.min.js"></script>
<ul class="parent">
<li><div class="anchor"></div></li>
<li><div class="anchor"></div></li>
<li><div class="anchor"></div></li>
<li>
<div class="anchor" id="anchor"></div>
<div class="popper" id="popper"></div>
</li>
</ul>
As seen above, the .popper div is overflowing body; causing a scrollbar to appear.
Removing the li makes Popper work as expected but I cannot do this as I'd lose my list. Using flex-start instead of -end also works correctly.
It's worth noting that the .popper and .anchor elements could be any width depending on what's in them.
Are there any workarounds or solutions for this behaviour? Preferably, without changing any markup.
I wrote some code with three things in mind:
Highlighting a selection's border using 'on click'.
Selecting one item will remove the highlight from the other item.
The ability to deselect each item on click.
I've managed to get everything working for the most part, but I don't particularly like how complex the code is for the radial dot that appears when one item is selected.
Below is an example of what I'm talking about, particularly I'm looking for ways to refactor the code below into something a little more legible (shorter).
$(this).children('.radial').children().toggleClass('checked').parents('.itembox')
.siblings().children('.radial').children().removeClass('checked');
Here's a working example for more context (line 10):
var raceInternet = false;
var racePhone = false;
var raceTv = false;
$(function() {
var $targetDiv = $('#race-internet > .itembox');
var $radialDot = $('.radial > .center-dot');
$targetDiv.on('click', function() {
$(this).toggleClass('user-selected').siblings().removeClass('user-selected');
//Is it possible to refactor Line 10?
$(this).children('.radial').children().toggleClass('checked').parents('.itembox').siblings().children('.radial').children().removeClass('checked');
if ($targetDiv.is('.user-selected')) {
raceInternet = true;
} else {
raceInternet = false;
}
})
})
.itembox-container {
display: flex;
}
.boxes-2 {
width: calc((100% - 25px)/2);
margin: 10px;
padding: 10px;
}
.itembox {
position: relative;
display: inline-block;
border: 5px solid #e8e8e8;
border-radius: 10px;
cursor: pointer;
}
.user-selected {
border: 5px solid #E16E5B;
}
.itembox h4 {
color: #22ddc0;
font-weight: 700;
}
span.price {
display: inline-block;
font-weight: 400;
float: right;
color: #22ddc0;
}
.itembox > ul {
list-style: none;
}
.itembox > ul > li {
line-height: 3;
}
.radial {
position: absolute;
float: right;
height: 35px;
width: 35px;
padding: 2px;
border: 5px solid #e8e8e8;
border-radius: 50%;
top: 43%;
right: 10px;
}
.center-dot {
display: none;
position: relative;
height: 21px;
width: 21px;
background-color: #E16E5B;
border-radius: 50%;
}
.checked {
display: block;
}
.prime-aux:first-of-type {
top: 150px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="container">
<!-- Primary Content Container -->
<div class="prime-aux">
<div id="race-internet" class="itembox-container">
<div class="itembox boxes-2">
<h4>Gigabit Internet <span class="price">$60/mo</span></h4>
<ul>
<li>1,000 Mbps</li>
<li>No data caps</li>
</ul>
<div class="radial">
<div class="center-dot"></div>
</div>
</div>
<div class="itembox boxes-2">
<h4>Basic Internet <span class="price">$25/mo</span></h4>
<ul>
<li>25 Mbps</li>
<li>No data caps</li>
</ul>
<div class="radial">
<div class="center-dot"></div>
</div>
</div>
</div>
</div>
</section>
<!-- Primary Content Container End -->
View on JS Fiddle
You can eliminate a lot of your jQuery by just leveraging CSS. Typically, if I want to toggle a feature, I have it either display: block; or display: none; based upon a CSS selector. Then, I just use jQuery to toggle the parent element's class name. So for example:
.item.selected .checkmark {
display: block;
}
.item .checkmark {
display: none;
}
$('.item').click(function(){ $(this).toggleClass('selected') });
JSFiddle