How do I toggle visibility of an entire dynamic row (flex wrap)? - javascript

Current Behavior
I have the following basic structure:
<section id="containers">
<div class="box" id="box1">
<div class="collapsible"><h2>Box 1</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box2">
<div class="collapsible"><h2>Box 2</h2></div>
<div class="content">Content</div>
</div>
<!-- ... dozens of .boxes ... -->
</section>
#containers is .display: flex; flex-wrap: wrap, so the number of boxes on any one row is dynamic. This is an important feature that must be maintained.
Here's a minimal working example:
$(document).ready(function() {
$(".collapsible").click(function() {
$( this ).next().slideToggle();
});
});
#containers {
display: flex;
flex-wrap: wrap;
gap: 1em;
}
.box {
min-width: 15em;
background: #888;
border: #555 1px solid;
overflow: hidden;
border-radius: 0.5em;
}
.collapsible {
background: #ccc;
cursor: pointer;
}
.collapsible h2 {
margin: 0;
margin-left: 16px;
font-size: 2rem;
}
.collapsible:hover {
background: #aaf;
}
.content {
margin: 0.5em;
margin-left: 16px;
display: none; /* Initially collapsed */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<p id="status"></p>
<section id="containers">
<div class="box" id="box1">
<div class="collapsible"><h2>Box 1</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box2">
<div class="collapsible"><h2>Box 2</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box3">
<div class="collapsible"><h2>Box 3</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box4">
<div class="collapsible"><h2>Box 4</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box5">
<div class="collapsible"><h2>Box 5</h2></div>
<div class="content">Content</div>
</div>
</section>
</body>
I can easily use .slideToggle() to toggle visibility of a sibling (.content) underneath one of the clickable .collapsible divs:
$(".collapsible").click(function() {
$( this ).next().slideToggle();
});
Desired Behavior and Remarks
What I'd like is, on click of any .collapsible div in a row, the entire row will be toggled. That is, every .content div on the same horizontal row as displayed in the current viewport.
This must handle rows with dynamic number of columns, including viewport resizing. I'm flexible on the precise behavior, though.
Is this possible? And how much will it hurt?🙂 Changes to the document structure (such as adding some sort of row container) are OK, and I don't mind using some JS/jQuery code of course. The main thing I can't do is hard code the number of columns, as I need my site to remain responsive on vertical phones and fullscreen desktop browsers.

Maybe the jQuery slideToggle() function is not the best method.
Since a row will be the same height , you may look at a method to set size from 0 to another value. anybox resized will stretch other boxes on the same row.
here an example of the idea, setting a max-height from 0 to 200px and a transition.
it toggles a class.
$(document).ready(function() {
$(".collapsible").click(function() {
this.classList.toggle('slide');// plain javascript to toggle a class
});
});
*{box-sizing:border-box}
#containers {
display: flex;
flex-wrap: wrap;
gap: 1em;
}
.box {
min-width: 15em;
background: #888;
border: #555 1px solid;
overflow: hidden;
border-radius: 0.5em;
}
.collapsible {
background: #ccc;
cursor: pointer;
}
.collapsible h2 {
margin: 0;
margin-left: 16px;
font-size: 2rem;
}
p{margin-top:0;}
.collapsible:hover {
background: #aaf;
}
.content {
margin:0 0.5em;
margin-left: 16px;
max-height:0; /* Initially collapsed */
transition:0.5s
}
.slide + .content{max-height:400px;/* which one got the class*/ color:darkred}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<p id="status"></p>
<section id="containers">
<div class="box" id="box1">
<div class="collapsible"><h2>Box 1</h2></div>
<div class="content"><p>Content</p></div>
</div>
<div class="box" id="box2">
<div class="collapsible"><h2>Box 2</h2></div>
<div class="content"><p>Content</p><p>Content</p></div>
</div>
<div class="box" id="box3">
<div class="collapsible"><h2>Box 3</h2></div>
<div class="content"><p>Content</p></div>
</div>
<div class="box" id="box4">
<div class="collapsible"><h2>Box 4</h2></div>
<div class="content"><p>Content</p><p>Content</p><p>Content</p></div>
</div>
<div class="box" id="box5">
<div class="collapsible"><h2>Box 5</h2></div>
<div class="content"><p>Content</p></div>
</div>
</section>
</body>
What you are missing here , is to set the height of the row to the height of the taller box, unless they will all be the same height.
For this, you can look for every box at the same top offset that the one clicked, and look for the tallest innercontent of the .contents and use this height .
Here comes the idea looking where each are standing and resets a max-height rule, you can inspire from too:
// defaut : supposed to be with no class 'slide' set on html elements
$(document).ready(function () {
let boxes = document.querySelectorAll(".collapsible");
let contents = document.querySelectorAll(".content");
$(".collapsible").click(function (event) {
let arrayContent = [];//reset
let hide=false;
for (i = 0; i < contents.length; i++) {
contents[i].style.maxHeight = "min-content";
let index = i;
let heightC = contents[i].offsetTop;
let boxOffset = contents[i].parentNode.offsetTop + 1;
// a few infos .add/remove what is needed
arrayContent.push({
index,
heightC,
boxOffset
});
contents[i].setAttribute("style", "");// resets inline style
}
let rowOffset = this.offsetTop;
if(this.classList.contains('slide')) {
hide=true;
}
let classState = this.classList;
arrayContent.forEach(function (obj) {
if (obj.boxOffset == rowOffset) {
if(hide == true) boxes[obj.index].classList.add('slide');/* reset needed if window resized => rows reorganized ? */
boxes[obj.index].classList.toggle("slide");
} else {
boxes[obj.index].classList.remove("slide");
}
});
});
});
* {
box-sizing: border-box
}
#containers {
display: flex;
flex-wrap: wrap;
gap: 1em;
}
.box {
min-width: 15em;
background: #888;
border: #555 1px solid;
overflow: hidden;
border-radius: 0.5em;
}
.collapsible {
background: #ccc;
cursor: pointer;
}
.collapsible h2 {
margin: 0;
margin-left: 16px;
font-size: 2rem;
}
p {
margin-top: 0;
}
.collapsible:hover {
background: #aaf;
}
.content {
margin: 0 0.5em;
margin-left: 16px;
max-height: 0;
/* Initially collapsed */
transition: max-height 0.5s!important
}
.slide~.content {
max-height: 400px;
/* which one got the class*/
color: darkred;
}
.collapsible:before {
content: attr(class)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<p id="status"></p>
<section id="containers">
<div class="box" id="box1">
<div class="collapsible">
<h2>Box 1</h2>
</div>
<div class="content">
<p>Content</p>
</div>
</div>
<div class="box" id="box2">
<div class="collapsible">
<h2>Box 2</h2>
</div>
<div class="content">
<p>Content</p>
<p>Content</p>
</div>
</div>
<div class="box" id="box3">
<div class="collapsible">
<h2>Box 3</h2>
</div>
<div class="content">
<p>Content</p>
</div>
</div>
<div class="box" id="box4">
<div class="collapsible">
<h2>Box 4</h2>
</div>
<div class="content">
<p>Content</p>
<p>Content</p>
<p>Content</p>
</div>
</div>
<div class="box" id="box5">
<div class="collapsible">
<h2>Box 5</h2>
</div>
<div class="content">
<p>Content</p>
</div>
</div>
</section>
</body>

Related

How to align boxes right under each other with flexbox

The point is that I want to implement this layout with html, css and js.
When you click on the button, the container should alternately become taller/smaller.
picture 1
picture 2
However, no fixed height should be passed, as is currently implemented.
Without using max-height, scaleY.
Also, text5 should be right under text2 and text6 right under text3 (as you can see in the picture) without using width or something similar.
The flex items should only be as large as the content.
I've also searched for solutions, but the search results weren't what I was looking for.
How can I transition height: 0; to height: auto; using CSS?
Flexbox not giving equal width to elements
Align divs under each other
let isOpen = false;
function toggleHeight() {
let row2 = document.getElementById('row2');
if(!isOpen){
isOpen= !isOpen;
row2.style.height = '120px';
//row2.style.height = 'auto';
}
else{
isOpen= !isOpen;
row2.style.height = '0';
}
}
.row1, .row2 {
display: flex;
gap: 10px;
background-color: #ccc;
}
.row2 {
height: 0;
transition: 0.25s ease;
overflow: hidden;
}
.col1 {
display:flex;
}
<div onclick="toggleHeight()">Click Me</div>
<div class="row1">
<div class="col1">
<div>
<div>text</div>
<div>text</div>
</div>
<div>
<div>text</div>
<div>text</div>
</div>
<div>
<div>text</div>
<div>text</div>
</div>
</div>
<div class="col2">
<div>text2</div>
<div>text2</div>
</div>
<div class="col2">
<div>text3</div>
<div>text3</div>
</div>
</div>
<div id="row2" class="row2">
<div class="col1">
<div>text4</div>
<div>text4</div>
</div>
<div class="col2">
<div>text5</div>
<div>text5</div>
</div>
<div class="col2">
<div>text6</div>
<div>text6</div>
</div>
</div>
I created exactly you wanted but the transition on height is not working!
const button = document.querySelector('button')
const row2 = document.querySelector('#row-2')
button.addEventListener('click', () => {
row2.classList.toggle('visible')
})
#import "https://cdn.jsdelivr.net/gh/KunalTanwar/normalize/css/normalize.inter.min.css";
body {
height: 100%;
display: grid;
place-items: center;
}
button {
padding: 4px;
border: 1px solid #c1c1c1;
}
.flex {
margin-top: 32px;
display: flex;
flex-direction: column;
}
.flex .row {
display: inherit;
column-gap: 16px;
}
.flex .row .col {
display: inherit;
padding-inline: 16px;
flex-direction: column;
}
.flex .row#row-2 {
height: 0px;
overflow: hidden;
background-color: red;
transition: height 250ms ease, overflow 250ms ease;
}
.flex .row#row-2.visible {
height: auto;
overflow: visible;
}
<div>
<button>Click Me!</button>
<div class="flex">
<div class="row" id="row-1">
<div class="col">
<span>texttexttext</span>
<span>texttexttext</span>
</div>
<div class="col">
<span>text2</span>
<span>text2</span>
</div>
<div class="col">
<span>text3</span>
<span>text3</span>
</div>
</div>
<div class="row" id="row-2">
<div class="col" style="align-self: center;">texttexttext</div>
<div class="col">
<span>text5</span>
<span>text5</span>
</div>
<div class="col">
<span>text6</span>
<span>text6</span>
</div>
</div>
</div>
</div>
P.S - remove padding-inline from .col.

Make progress bars responsive

I have these progress bars which are suppose to work as ratings and they look good:
I have applied some CSS and I change their width with JavaScript by reading values from text files.
But are not responsive at all and whenever the window is resized:
HTML:
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<div class="second-part">
<div class="row">
<div class="side">
<div>5 stars</div>
</div>
<div class="middle">
<div class="bar-container">
<div class="bar11" style="width: 10% ; height: 18px; background-color: gold;"></div>
</div>
</div>
<div class="side right">
<div class="p11">150</div>
</div>
<div class="side">
<div>4 stars</div>
</div>
<div class="middle">
<div class="bar-container">
<div class="bar12" style="width: 10% ; height: 18px; background-color: gold;"></div>
</div>
</div>
<div class="side right">
<div class="p12">63</div>
</div>
<div class="side">
<div>3 stars</div>
</div>
<div class="middle">
<div class="bar-container">
<div class="bar13" style="width: 10% ; height: 18px; background-color: gold;"></div>
</div>
</div>
<div class="side right">
<div class="p13">15</div>
</div>
<div class="side">
<div>2 stars</div>
</div>
<div class="middle">
<div class="bar-container">
<div class="bar14" style="width: 10% ; height: 18px; background-color: gold;"></div>
</div>
</div>
<div class="side right">
<div class="p14">6</div>
</div>
<div class="side">
<div>1 star</div>
</div>
<div class="middle">
<div class="bar-container">
<div class="bar15" style="width: 10% ; height: 18px; background-color: gold;"></div>
</div>
</div>
<div class="side right">
<div class="p15">20</div>
</div>
</div>
</div>
CSS:
* {
box-sizing: border-box;
}
/* Three column layout */
.side {
float: left;
width: 15%;
margin-top:10px;
}
.middle {
margin-top:10px;
float: left;
width: 70%;
}
/* Place text to the right */
.right {
text-align: left;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
/* The bar container */
.bar-container {
width: 90%;
background-color: #f1f1f1;
text-align: center;
color: white;
}
/* Responsive layout - make the columns stack on top of each other instead of next to each other */
#media (max-width: 400px) {
.side, .middle {
width: 100%;
}
.right {
display: none;
}
}
JavaScript to change progress bar width:
var par1 = 4;
for(var i = 10; i < 16; i++) {
$('.p' + (i+1)).text(table[0][par1]);
$('.bar' + (i+1)).css("width", table[0][par1]);
par1++;
}
How could I make it more responsive? Thank you in advance!
I recommend using css flexbox, so the items wrap instead of overlap when the page is resized. You could use media queries with this to adjust the size of the items and container so they don't overlap/wrap. This site has a good explanation of flexbox: A Complete Guide to Flexbox.

Flex-wrap with equal width

I use flex-wrap in CSS. I want a layout that accept these conditions:
Use flex-wrap.
Use the same width of all the boxes/child elements.
Use the width of the largest child element. No fixed widths.
It's not required to fill the width of the container.
In the jsfiddle it would probably be 1 or 2 items on each row.
Is it possible with some clever css flex solution or do I need to use javascript?
https://jsfiddle.net/swysdq22/
.wrap {
width: 300px;
background: #fff;
}
.narrow {
display: flex;
flex-wrap: wrap;
}
.narrow > * {
background: #ccc;
border: 2px solid #fff;
padding: 10px;
color: #fff;
}
<div class="wrap">
<div class="narrow">
<div class="item1">
Longest sentence.
</div>
<div class="item2">
Short text.
</div>
<div class="item3">
Another text.
</div>
<div class="item1">
Longest sentence.
</div>
<div class="item2">
Short text.
</div>
<div class="item3">
Another text.
</div>
</div>
</div>
You can use map() and get() to return array of widths and then with Math.max you can find maximum value in that array and apply it as width on each item
var w = $('.narrow > div').map(function() {
return $(this).width();
}).get();
var max = Math.max.apply(null, w);
$('.narrow > div').width(max);
.wrap {
width: 300px;
background: #fff;
}
.narrow {
display: flex;
flex-wrap: wrap;
}
.narrow >* {
background: #ccc;
border: 2px solid #fff;
padding: 10px;
color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrap">
<div class="narrow">
<div class="item1">
Longest sentence.
</div>
<div class="item2">
Short text.
</div>
<div class="item3">
Another text.
</div>
<div class="item1">
Longest sentence.
</div>
<div class="item2">
Short text.
</div>
<div class="item3">
Another text.
</div>
</div>
</div>

Prevent Divs from starting a new line with percentage width

I have several divs inside another div and I want the user to be able to resize those divs but they should stay in percentage width. This example shows 6 divs with 18% width which is more than 100% together and the 6th div starts a new line:
http://jsfiddle.net/v3o3vqqo/
div {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.container {
width: 100%;
}
.inner {
width: 18%;
float: left;
margin: 1px;
}
.red {
background-color: red;
}
.orange {
background-color: orange;
}
.yellow {
background-color: yellow;
}
.green {
background-color: green;
}
.blue {
background-color: blue;
}
.purple {
background-color: purple;
}
<h4>The goal</h4>
<p>Get all the boxes to fit on one line, with equal width.</p>
<div class="container"><div class="inner red"> </div><div class="inner orange"> </div><div class="inner yellow"> </div><div class="inner green"> </div><div class="inner blue"> </div><div class="inner purple"> </div>
<!-- Plus arbitrarily many more boxes... -->
</div>
I want it to show 2 children since their width is 100% togheter and 2 children in overflow (which means I need to scroll to see the other.
The problem is, if the parent's width is 100% and the sum of the children is more than 100%, they start a new line rather then cause a horizontal scroll.
So my question is how do I cause a horizontal scroll?
You can't achieve this with floats. You will have to use different layouts.
You can use flexbox. By default it has flex-wrap: nowrap, so there is no line wrap.
You will need to use flex-shrink: 0 to prevent shrinking when the flex items occupy more space than the available one.
.container {
display: flex;
overflow: auto;
}
.inner {
width: 18%;
height: 2em;
flex-shrink: 0;
}
<h4>The goal</h4>
<p>Get all the boxes to fit on one line, with equal width.</p>
<div class="container">
<div class="inner" style="background: red"></div>
<div class="inner" style="background: orange"></div>
<div class="inner" style="background: yellow"></div>
<div class="inner" style="background: green"></div>
<div class="inner" style="background: blue"></div>
<div class="inner" style="background: purple"></div>
</div>
Alternatively, you can use inline-block, and prevent line wrapping with white-space: nowrap. However, be aware of How to remove the space between inline-block elements?
.container {
overflow: auto;
white-space: nowrap;
}
.inner {
display: inline-block;
width: 18%;
height: 2em;
}
<h4>The goal</h4>
<p>Get all the boxes to fit on one line, with equal width.</p>
<div class="container">
<div class="inner" style="background: red"></div
><div class="inner" style="background: orange"></div
><div class="inner" style="background: yellow"></div
><div class="inner" style="background: green"></div
><div class="inner" style="background: blue"></div
><div class="inner" style="background: purple"></div>
</div>
One possible solution to what I understand your problem to be is the following.
Basic idea: use display: inline-block, and prevent white space issues by putting the closing > on the next line.
div {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.container {
width: 100%;
}
.inner {
display: inline-block;
width: 20%;
}
.red {
background-color: red;
}
.orange {
background-color: orange;
}
.yellow {
background-color: yellow;
}
.green {
background-color: green;
}
.blue {
background-color: blue;
}
.purple {
background-color: purple;
}
<h4>The goal</h4>
<p>Get all the boxes to fit on one line, with equal width.</p>
<div class="container">
<div class="inner red"> </div
><div class="inner orange"> </div
><div class="inner yellow"> </div
><div class="inner green"> </div
><div class="inner blue"> </div>
<!-- Plus arbitrarily many more boxes... -->
</div>

Aligning boxes(divs) of equal size in html symmetrically according to their number

I want to make an HTML page that contains a box at the top and a certain number of boxes being dynamically generated using jquery, the number of boxes at the bottom of top box can be 4 at max.
I want to align these boxes dynamically and symmetrically in the html page. I am using angularjs's ng-repeat to generate boxes. I want the sizes of the boxes to remain same but arrange them symmetrically on the html page.
currently i am using angular js to dynamically create boxes and align them using col-md class of bootstrap. but this makes the size of boxes to change when the number of boxes change.
html code
<div id="header-wrapper" class="container vscrolling_container">
<div id="header" class="container vscrolling_container">
<div id="logo">
<h1 class="page-head-line" id="visionh"><a>Vision</a></h1>
<p id="visionp"><a rel="nofollow">{{visiontext}}</a></p>
</div>
</div>
</div>
<div class="row" id="missionstart">
<div ng-class="missioncount[missions.length]" ng-repeat="mission in missions" style="opacity: 0.9">
<div class="dashboard-div-wrapper" ng-class="bkclr[$index]">
<h1 id="{{mission.id}}" style="color: #000">{{mission.missionInfo}}</h1>
<div class="progress progress-striped active">
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 100%"></div>
</div>
<ul>
<li id="{{missioncontent.id}}" ng-repeat="missioncontent in mission.missionContent">
<p style="text-align: left">{{missioncontent.info}}</p>
</li>
</ul>
</div>
</div>
</div>
java script code
'use strict';
var mission_vision_mod = angular.module('myApp.mission_vision', ['ngRoute']);
mission_vision_mod.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/mission_vision', {
templateUrl: 'partials/mission_vision/mission_vision.html',
controller: 'mission_visionCtrl'
});
}]);
mission_vision_mod.controller('mission_visionCtrl', ['$scope','$http', function($scope, $http) {
$scope.visiontext = "Here is the content of vision";
$scope.bkclr = ['bk-clr-one','bk-clr-two','bk-clr-three','bk-clr-four'];
$scope.progressbar = ['progress-bar-warning','progress-bar-danger','progress-bar-success','progress-bar-primary'];
$scope.missioncount = ['col-md-0','col-md-12','col-md-6','col-md-4','col-md-3','col-md-2.5','col-md-2'];
$http.get('m_id.json').success(function(data){
$scope.missions = data;
$scope.len = data.length;
});
}]);
I have created a quick jsfiddle
HTML Content:
<div class="container">
<div class="header"></div>
<div class="content">
<div class="contentBox"></div>
<div class="contentBox"></div>
</div>
</div>
<br/><br/><br/>
<div class="container">
<div class="header"></div>
<div class="content">
<div class="contentBox"></div>
<div class="contentBox"></div>
<div class="contentBox"></div>
<div class="contentBox"></div>
</div>
</div>
Related CSS:
.container div {
height: 100px;
}
.header {
border: 1px solid black;
width: 75%;
margin: 5px auto;
}
.content {
text-align: center;
}
.contentBox {
border: 1px solid black;
width: 20%;
display: inline-block;
box-sizing: border-box;
}
Caution: I have used plain CSS for this demo.
Hope this helps you.
flexbox can do this
.wrap {
width: 80%;
margin: auto;
border: 1px solid black;
margin-bottom: 25px;
}
.top,
.item {
width: 100px;
height: 50px;
background: red;
}
.top {
display: inline-block;
}
.flex {
display: flex;
justify-content: space-around;
}
<div class="wrap">
<div class="flex">
<div class="top"></div>
</div>
<div class="flex">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
</div>
JSfiddle Demo

Categories