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>
Related
I am bit troubled in aligning the content as shown in the image. Along with that I need to redirect to page 1 by clicking on content section and redirect to page 2 by clicking the icon separately. How can I solve it using javascript?
These things can be achieved without the use of JavaScript besides the point that it's generally bad practice to manipulate DOM with JavaScript as much can be done with css rules, animations, basic html.
To align the icon first you should try these css rules applied on icon element:
.your-icon {
top: 0;
right: 0;
}
Assuming your icon in html is declared like this:
<img src="foo.img" class="your-icon">
Although it depends on current rules applied (e.g. you should use other rule set if flex is enabled on parent element). Perhaps spacing with fixed values between content section is involved that is pushing the icon out of the way (padding, margin rules applied?).
As for navigation html href is good enough
.a {
/* width:400px; */
height: 300px;
border: 1px solid;
}
.b {
width: 100%;
float: left;
/* border:1px solid red; */
}
.c {
width: 70%;
/* border:1px solid; */
float: left;
/* display:flex; */
float: left;
}
.c div{padding: 5px;}
.d {
border: 1px solid;
}
.box {
width: 29%;
float: left;
border: 1px solid;
height: 100%;
}
.rTable {
display: block;
width: 100%;
}
.rTableHeading,
.rTableBody,
.rTableFoot,
.rTableRow {
clear: both;
}
.rTableHead,
.rTableFoot {
/* background-color: #DDD; */
font-weight: bold;
}
.rTableCell,
.rTableHead {
/* border: 1px solid #999999; */
float: left;
height: 17px;
overflow: hidden;
padding: 5px;
width: 20%;
}
.rTable:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
<div class='a'>
<div class='b'>
<div class='c'>
<div>Name</div>
<div>Address</div>
<div>PIN</div>
</div>
<div class='box'>ICON</div>
</div>
<div class='d'>
<div class="rTable">
<div class="rTableRow">
<div class="rTableHead"><strong>Time</strong></div>
<div class="rTableHead"><span style="font-weight: bold;">Duration</span></div>
<div class="rTableHead"><strong>Amount</strong></div>
<div class="rTableHead"><strong>Position</strong></div>
</div>
<div class="rTableRow">
<div class="rTableCell">5</div>
<div class="rTableCell">2</div>
<div class="rTableCell">3</div>
<div class="rTableCell">4</div>
</div>
</div>
</div>
</div>
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!
My team and I are having trouble stacking a Dropdown component on our page. Essentially, we want the Dropdown to slide down underneath the top-nav when the button is clicked, but as it slides down, it should be positioned above everything else: the sub-nav and the content below.
Currently, the Dropdown is positioned as absolute and the animation is performed with a transform: translateY(). We've tried positioning the elements outside of it as relative (the outer <ul>, <nav>, and <div id="top-nav"> elements that are bolded) with a higher z-index to ensure the dropdown stays below it, but so far it hasn't worked.
We're also not able to modify any of the CSS or structure of the div#content below, but we do have flexibility as to where we can place the Dropdown structurally in the #header.
EDIT: Tried my best to recreate the scenario with JSFiddle here: https://jsfiddle.net/4zaas4sq/
Here's roughly what our markdown looks like:
<body>
<div id="header">
<div>
**<div id="top-nav">**
<div>
**<nav>**
<ul></ul>
**<ul>**
<li>
<DROPDOWN>
<button onClick={toggleDropdown}>Log In</button>
<div className={(this.state.show && 'show})>
<ul></ul>
</div>
...
</DROPDOWN>
</li>
<li></li>
</ul>
</nav>
</div>
</div>
<div id="sub-nav">
...
</div>
</div>
</div>
<div id="content">
</div>
</body>
Here's a wireframe depicting the final state of the dropdown.
Any help or suggestions would be appreciated!
I used max-height property.I didn't change a lot of things in your code.In JS code you will see main changes.Let me know if this solution is what you want.Thanks :)
In html code add class="hideItem" in the divider with id="dropdown" like this:
<div id="dropdown" class="hideItem">
JS code
$(document).ready(function() {
$('#dropdown-button').click(function() {
if( $("#dropdown").hasClass( 'hideItem' )){
$( "#dropdown" ).css( 'max-height' , '100%' );
$("#dropdown").removeClass( 'hideItem' );
$("#dropdown").addClass( 'showItem' );
}else{
$( "#dropdown" ).css( 'max-height' , '0' );
$("#dropdown").addClass( 'hideItem' );
$("#dropdown").removeClass( 'showItem' );
}
});
});
css code
html, body {
height: 100%;
}
#top-nav {
background-color: mediumpurple;
width: 100%;
}
.nav {
display: flex;
justify-content: space-between;
}
.inner-left-nav {
list-style: none;
display: flex;
}
.inner-left-nav li {
padding: 5px;
border: 1px solid black;
}
.inner-right-nav {
display: flex;
list-style: none;
margin: 0;
}
.inner-right-nav li {
align-items: center;
padding: 0 5px;
}
.dropdown-container {
display: flex;
align-items: center;
height: 100%;
}
#dropdown {
position: absolute;
top: 70px;
right: 100px;
max-height: 0;
overflow-y: hidden;
transition: max-height 1s ease-in-out;
background-color: mediumseagreen;
}
#dropdown.show {
visibility: visible;
transform: translateY(0%);
transition: visibility 0s, transform 0.3s;
}
#dropdown-button {
border: 1px solid black;
background: transparent;
padding: 0 20px;
cursor: pointer;
}
.dropdown-list {
padding: 0;
list-style: none;
}
#sub-nav {
display: flex;
justify-content: space-between;
background-color: grey;
}
#content {
background-color: azure;
width: 100%;
height: 100%;
}
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
So I'm building a website for a restaurant and I'm in a pickle. I'm trying to create the menu there. The idea is to align the food name to the left, the price to the right and fill the gap between them with dots. Like this
Hamburger ............................................ $ 4.00
XXL Hamburger .................................... $ 4.00
Milkshake .............................................. $ 4.00
I found a couple of solutions, which only work if you have a background with one color and no texture. The idea was to fill the whole line with dots and set the name/price background span with the same color as the site background, so the dots wouldn't show. But I have a picture for the background.
I'm not going to post my code here, because it wouldn't really matter or help.
Is it even possible? Doesn't have to be css only, might as well be done with JavaScript.
I am kinda late, but you can quite easily do it with a radial-gradient:
.col {
display: inline-block;
vertical-align: top;
}
.names span {
width: 200px;
display: flex;
}
.prices span {
display: block;
text-align:right;
}
.names span:after {
content: "";
display: inline-block;
height: 1em;
flex-grow: 1;
background: radial-gradient(black 25%, transparent 25%) scroll repeat-x bottom left/5px 5px;
}
<div class='names col'>
<span>Hamburger</span>
<span>Hot Dogs</span>
<span>Superman Hamburger</span>
</div>
<div class='prices col'>
<span>$1.00</span>
<span>$0.50</span>
<span>$400.00</span>
</div>
JSFiddle Demo
It's easy to do with some simple javascript and css, here's a fiddle: jsfiddle
The key is to set the width of the div that holds the dots to the width of the column minus the width of the food name minus the width of the price, and to make sure there are more than enough dots to cover the distance, and to set overflow: hidden for the dot div.
$(".menu-row").each(function(index, element) {
var menuRowWidth = $(element).width();
var foodItemWidth = $(element).children('.food-item').width();
var priceWidth = $(element).children('.price').width();
var $dotFiller = $(element).children('.dot-filler');
var dotFillerWidth = menuRowWidth - foodItemWidth - priceWidth;
$dotFiller.width(dotFillerWidth + "px");
});
Then float the item and dot div left, the price right, all within a set width column. It's also important that overflow: hidden is set for the dots, because when we set the width of that div in javascript we want all extra dots to just be cut off. The CSS:
.food-item {
float: left
}
.dot-filler {
overflow: hidden;
width: 0;
float: left;
}
.price {
float: right;
}
.menu-row {
width: 400px;
}
Then structure your html as follows:
<div class="menu-row">
<div class="food-item">Steak</div>
<div class="dot-filler">............................................................................................</div>
<div class="price">$18.00</div>
</div>
<div class="menu-row">
<div class="food-item">Hamburger</div>
<div class="dot-filler">............................................................................................</div>
<div class="price">$8.00</div>
</div>
You can use a wrapper to set a fix width of your Name + Dots.
The css will look like this:
.wrapper {
width: 300px;
overflow: hidden;
display: inline-block;;
white-space: nowrap;
}
The HTML like this:
<div>
<ul class="noDisc">
<li>
<div class="wrapper">
<span>HAMBURGER </span>
<span>...............................................................</span>
</div>
<span>$ 40.00</span>
</li>
<li>
<div class="wrapper">
<span>FRIED CHIKEN </span>
<span>...............................................................</span>
</div>
<span>$ 13.00</span>
</li>
<li>
<div class="wrapper">
<span>STEAK ON A STICK </span>
<span>...............................................................</span>
</div>
<span>$ 99.00</span>
</li>
</ul>
</div>
Live sample:
fiddle
Use display:table; and display: table-cell; for the divs inside the list-elements and border-bottom: Xpx dotted black; for the dots.
ul{
margin: 0;
padding: 0;
}
ul li{
display: table;
width: 100%;
}
ul li div {
display: table-cell;
}
ul li div.food {
padding-right: 5px;
}
ul li div.dots {
border-bottom: 1px dotted #000;
width: 100%;
position: relative;
top: -4px;
}
ul li div.price {
padding-left: 5px;
}
<ul>
<li>
<div class="food">Spaghetti</div>
<div class="dots"> </div>
<div class="price">10.00$</div>
</li>
<li>
<div class="food">Spaghetti</div>
<div class="dots"></div>
<div class="price">10.00$</div>
</li>
<li>
<div class="food">Spaghetti</div>
<div class="dots"></div>
<div class="price">10.00$</div>
</li>
</ul>
Thanks. I used what you had here and improved on it. This code is meant for woocommerce product items, but can be edited for whatever you need. $containerElement is the element you are measuring the width of.
/**
* dotFiller
* adds dynamic dot leaders between product title and count element (<mark>)
* #return void
*/
var dotFiller = function(){
var $containerElement = $('ul.products li.product.has-children h2'),
df = '<div class="df">.....................................................................</div>';
$containerElement.each(function(i,el){
var $el = $(el),
w = $el.width(),
mw = $el.find('mark').width(),
tw = $el.find('span').width(),
dfw = (w - mw - tw) - 24;
// if its not there, lets add it
if (!$(el).has('.df').length){
$el.find('span').after(df);
}
$el.find('.df').css('width',dfw + "px");
});
};
dotFiller();
With this code, you can update/ recalculate on resize like so :
$('window').on('resize',function(){ dotFiller(); });
And here is my css for the internal elements:
mark {
background-color: transparent;
color: $secondary;
display: inline-block; float: right;
font-weight: normal;
}
div.df {
overflow: hidden;
display: inline-block;
margin-left: 10px;
position: relative;
top: 2px;
font-weight: normal;
opacity: 0.8;
}
I hope this helps someone!
Use a div that has a flex spacer with a border-bottom to achieve easy leader dots... The flex layout seems to be the most elegant solution. No pseudo-elements, or left and right block display, etc... Very simple...
HTML
<div class="list-item">
<div class="header-row">
<h4>Menu</h4>
</div>
<br>
<div class="list-item-row">
<div class="left">Hamburger</div>
<div class="dots"></div>
<div class="right">$5.00</div>
</div>
<div class="list-item-row">
<div class="left">Hamburger (XXL)</div>
<div class="dots"></div>
<div class="right">$7.50</div>
</div>
<div class="list-item-row">
<div class="left">Milkshake</div>
<div class="dots"></div>
<div class="right">$3.50</div>
</div>
<div class="list-item-row">
<div class="left">Pickle</div>
<div class="dots"></div>
<div class="right">Free</div>
</div>
</div>
CSS
#import url("https://fonts.googleapis.com/css?family=Lato|Montserrat:400,700|Roboto:400,700");
* {
margin: 0;
padding: 0;
}
button {
font-family: "Roboto";
font-size: 16px;
padding: 5px;
border-radius: 3px;
border: solid #424242 1px;
}
.list-item {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
margin: 20px;
font-family: "Lato";
background: #f0f0f0;
padding: 10px;
border: solid #e0e0e0 1px;
}
.list-item-row,
.header-row {
width: 100%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
.left,
.right {
font-family: "Roboto";
}
.right {
color: blue;
}
}
.dots {
flex: 1 0 0;
border-bottom: dotted 2px #b0b0b0;
margin-left: 1em;
margin-right: 1em;
}
See Codepen here => https://codepen.io/anon/pen/vVZmxB