Have side column appear in the middle on small screens - css

I'm trying to create a layout using twitter bootstrap's grid with three blocks A, B and C which appear in that order when they are stacked on small screens. On larger screens however, the middle block B should move to the right and A and C appear stacked to the left.
The catch is, that the blocks have unknown heights and in fact, the heights can change dynamically due to collapsibles, tabs, etc. In particular, I get into trouble when B is higher than A.
The small screen layout is supposed to look like this: (In the picture B is a very high block as indicated by using two brackets.).
[A]
[B]
[B]
[C]
On wider screens I want it to render like this:
[A][B]
[C][B]
The most straight forward code would be this:
<div class="row">
<div class="col-md-6">A</div>
<div class="col-md-6">B</div>
<div class="col-md-6">C</div>
</div>
But then the layout contains an ugly gap between A and C because C is placed below the end of B.
[A][B]
[B]
[C]

.row {
display: flex;
flex-wrap: wrap
}
will be a solution, in addition you can define order style for every div for example on smaller screen if you want B to be displayed first
#media screen and (max-width: 768px) {
div.B {
order: 1
}
dic.A {
order: 2
}
div.C {
order: 3
}
}
and B will be first A will be second and C third
for more information about flexbox visit - https://css-tricks.com/snippets/css/a-guide-to-flexbox/

Related

CSS: Wrap several elements at once to next line once width is exceeded

This is a pretty specific issue and I wouldn't be surprised if it's impossible to solve. Let's say I have the following html, which I cannot change at all:
<box>
<tab class="A" id="A1">...</tab>
<tab class="A" id="A2">...</tab>
<tab class="A" id="A3">...</tab>
...
<tab class="B" id="B1">...</tab>
<tab class="B" id="B2">...</tab>
<tab class="B" id="B3">...</tab>
...
</box>
The amount of A and B tabs varies. Using only CSS, I would like to achieve the following behavior:
1: The tabs have to be arranged in rows, and wrap to the next row once they exceed the width of the box (which is only constrained by screen width). (This is the easy part. I already know how to do it with flex, but I'm not committed to one specific method.)
|A1 A2 A3 B1|
|B2 B3 |
2: But what I would like to see is for the B elements all wrap to the next line at once, as if they were one element. I also found a way to do this by inserting an invisible line break element with ::before and assigning it an "order" value that puts it between A3 and B1.
|A1 A2 A3 |
|B1 B2 B3 |
3: However, this means there are always a minimum of 2 lines, for each type of element. The behavior I'm trying to achieve is that if all elements do fit inside the width of the box, they should all remain on the same line, and only wrap in the manner described in 2 once the width is exceeded. Like this:
|A1 A2 B1 B2|
New tab is added:
|A1 A2 |
|B1 B2 B3 |
Without reformatting the HTML, implementing a few Javascript conditional if-statements might make it easier since you are still able to target the className directly.
If you are able to reformat the HTML in some way, it might be easier to control with creating 2 new HTML div elements in the box element (1 surrounding each different class type) in order to control easier with flex-box.
I found a solution. Assuming that the width of the box fits exactly 4 tabs (This should be mathematically generalizable to other cases of course. My actual case is much more complicated, with the box width fitting roughly 10 B-tabs, and B-tabs being 4 times as wide as A-tabs. I'm simplifying here for comprehensibility.):
The box is styled as a flex-box:
box {
display: flex;
flex-wrap: wrap;
overflow-y: auto;
}
This inserts the line break with order value 1 (by default this is sorted after elements without the order property):
box::after {
display: flow-root list-item;
order: 1;
content: "";
flex-basis: -moz-available;
height: 0px;
}
This tells every B element immediately after an A element to be sorted after the line break iff A and B elements together exceed the line (this will require at minimum as many comma-separated selectors as the number of B-tabs that can be fit into the width of the box):
.A:nth-child(n+4) + .B:nth-last-of-type(n+1),
.A:nth-child(n+3) + .B:nth-last-of-type(n+2),
.A:nth-child(n+2) + .B:nth-last-of-type(n+3),
.A:nth-child(n+1) + .B:nth-last-of-type(n+4)
{ order: 2; }
This tells the subsequent elements to do the same:
.A:nth-child(n+4) + .B:nth-last-of-type(n+1) ~ *,
.A:nth-child(n+3) + .B:nth-last-of-type(n+2) ~ *,
.A:nth-child(n+2) + .B:nth-last-of-type(n+3) ~ *,
.A:nth-child(n+1) + .B:nth-last-of-type(n+4) ~ *
{ order: 2; }
Theoretically it should be possible to combine these two blocks via nesting:
.A:nth-child(n+4) + .B:nth-last-of-type(n+1),
.A:nth-child(n+3) + .B:nth-last-of-type(n+2),
.A:nth-child(n+2) + .B:nth-last-of-type(n+3),
.A:nth-child(n+1) + .B:nth-last-of-type(n+4)
{ order: 2;
~ * { order: 2; }
}
However, in my case that didn't work. I'm trying to style my browser UI, and apparently it doesn't accept nesting.
If anyone has a suggestion for how to further streamline this, it is more than welcome.

Arrange some items in last row using grid instead of flex

Previously I made this question.
I try the solution linked in the similar question already made (using grid instead of flex) but I have some problems.
I have to build a grid of plots (bar charts). My dataset could be aggregated by Day or by Month.
Obviously if the data is aggregated by months, the number of bars is much smaller than the number of bars in case the data is aggregated by day.
The width of the plot is proportional to the number of bars.
To show my problem I created these simple images: each rectangle represents a bar chart (is like a placeholder).
The larger rectangles represent the bar charts in the case in which the data are aggregated by Day, the narrower bar charts instead represent the bar charts when the data are aggregated by Month.
In this example I suppose that my dataset is composed by 6 chart.
I know the width of the graph because it is simply the number of bars for the width of the bar.
I would like the data to be positioned automatically.
In each row there cannot be more than two graphs, between a graph and the other there must be some space and the final row (if it is not complete) must have the charts aligned to the left. The last request is the reason why use Flex is not correct, so I switched to Grid.
So, I want that if data are aggregated by Day, charts would like:
If data are aggregated by Month, charts would like:
I try CSS grid using this code:
<div
className="relative ba b--black"
style={{
display: 'grid',
gridTemplateColumns: `repeat(auto-fit, minmax(45%, 1fr))`,
gridAutoRows: SVG_HEIGHT,
gridGap: '10px 5px', // space h, space v
}}
>
{
data.map((plot, plotIdx) => {
return (
<div
key={plotIdx}
className="ba b--black overflow-x-auto"
onScroll={this.handleHorizontalScroll(plotIdx)}
>
<svg
width={SVG_WIDTH}
height={CHART_HEIGHT + MARGINS.bottom + MARGINS.top}
>
// other code...
</svg>
</div>
)
})
}
</div>
(Each chart can have a large width so there is a horizontal scroll).
The problem now is that the cell of each plot is always the same even if the real dimension if the chart is smaller than the container.
Data aggregated by Day:
Data aggregated by Month:
If I use SVG_WIDTH in place of 45% in the minmax statement, I get:
in case data are aggregated by day, and this one in case data are aggregated my month:
I didn't use px, thanks! Now it works in the case of aggregation by month but not the case of aggregation by day.
by day (doesn't work):
by month (works):
The problem is that every graph should have a horizontal scroll in fact if the SVG_WIDTH is greater than the size of the father div, then I need to add a scroll.
Here is the code I used:
<div
className="relative ba b--black"
style={{
display: 'grid',
gridTemplateColumns: `repeat(auto-fit, minmax(${SVG_WIDTH}px, 1fr))`,
gridAutoRows: PLOT_HEIGHT,
gridGap: '10px 5px', // space h, space v
}}
>
{data.map((plot, plotIdx) => {
const yScale = this.yScale(plot)
const yAxisTicks = yScale.ticks(4)
return (
<div
key={plotIdx}
ref={plotElem => (this.plots[plotIdx] = plotElem)}
className="ba b--black overflow-x-auto"
onScroll={this.handleHorizontalScroll(plotIdx)}
>
// ...
</div>
)}
)}
</div>
Use SVG_WIDTH in place of 45% in the minmax statement.
As for the overflow problem that show up for big values, there is a workaround described here, although it is a bit hacky. Adapted to your case, minimum two columns:
#media (max-width: ${SVG_WIDTH * 2 + 10}px) {
.container { grid-template-columns: repeat(2, 1fr); }
}
Where 10 is your grid-gap. And the .container takes full width of the page with body margin reset to 0. Otherwise also add the default margin of the body tag (8px left and right) and the sidebar width if you have one. You can also use CSS calc() if your sidebar or layout is adaptive, but the support for calc() in media queries is limited to very recent browsers currently.

Angularjs calling clearfixes based on screen size with ng-if

I want my columns to display in 3 columns per row on medium and larger screen sizes (col-md-4), 2 columns per row on smaller screen sizes, and 1 column per row on extra small (col-sm-6), but when I view my app on my tablet, I get floating columns in the 2 column rows. I know its possible to use a clearfix class with ng-if to tell it to make a column every so many rows, but if I use:
<div class='clearfix' ng-if='$index % 2 == 0'>
it will make my columns create a new row every 2 columns, even on larger screens, which isn't what I want. Is it possible to make ng-if only add the clearfix after 2 columns strictly on the col-sm class, and not on the col-md class?
Yes you can do it by listing to window resize event, Based on the screen inner width you can attach or detach the div.clearfix to the dom using ng-if.
EX:
Controller:
maintain a variable to represents the window width $scope.windowWidth
app.controller('MainCtrl', function($scope, $window, $timeout) {
$scope.windowWidth = $window.innerWidth;
$window.onresize = function(event) {
$timeout(function() {
$scope.windowWidth = $window.innerWidth;
});
};
});
View:
Attach the div IF windowWidth <= 768 ELSE Remove the div from the DOM.
<div class="tab-only clearfix" ng-if="windowWidth <= 768">
This will show only on min-width : 768px
</div>
here is a DEMO
Please search for if there is any css fixes for this.

MVC razor get div width and check for condition

I have a view which should be responsive.My model has two properties leftPosition and topPosition.Now I want if the width of my outer div is less than 714 the topPosition and leftPosition are set to 0 else they will have the values which they receve from model.But how to check that outer div width in mvc razor syntax.I have tried to use javascript and set mvc razor variable but I found that its not possible to do such thing as asp.net is server-side and javascript is client side.
mvc partial view
#foreach (var item in Model)
{
if (Request.Browser.ScreenPixelsWidth < 1000)
{
{item.TopPosition = 0;}
{item.LeftPosition = 0;}
}
Html.RenderPartial(#item.TileName, #item);
}
Instead of this Request.Browser.ScreenPixelsWidth I want to use something like ("#desktopTilesContiner").Width,which I can use in my if condition.In my image I have different tiles - Labs,Pathology,Procedures,Medications.These are set according to their top and left position but on mobile device I just want they come in one column ignoring their top and left position.Something like the image two.
Your Request.Browser.ScreenPixelsWidth < 1000 can be taken care of with css media queries, like so:
#media all and (max-width: 1000px) { *content here* }
I would also recommend to not use position: absolute ( thus negating the need for the whole top / left positioning ) and let the DOM elements flow naturally.

css inline-block div positioning

I'm trying to fix a positing issue in a responsive design.
I have a container div, containing 4 (but it could be more or less) divs that are displayed as inline-block. I would like to know how to control the number of divs per line when the page is resized (with CSS, if it's possible). For example, when 4 containees no longer fits in the container, the last one is moved to second line. I would like in that case to have 2 containees in the first line and 2 containees in the second line. I dont know how to do that. Your help would be welcomed!
EDIT:
it could also be 6 containees, in the case the layout would be:
- 1 line of 6 blocks if it fits
- 2 lines of 3 blocks
- 3 lines of 2 blocks
- 6 lines of one
the number of containees is variable. I just want to have the same number of containees per line
the html:
<div class="container">
<div class="containee"></div>
<div class="containee"></div>
<div class="containee"></div>
<div class="containee"></div>
</div>
the css:
.containee {
width:200px;
height:200px;
display:inline-block;
background-color:tomato
}
the example can be seen here : http://cssdesk.com/uGLbq
(PS : I tried to find the solution searching the web but I dont really know the good keywords related with this topic)
You can't with CSS (AFAIK).
You can do "the math" dynamically with Javascript in real time.
In your case,
you known the width of one block (in that moment),
you can easily get the window width (in that moment),
you know the number of your block (in that moment);
Simply apply ( (1) the first time you open the page, and (2) every time the number of blocks changes, or (3) the resolution changes) the algorithm in the following code:
// EXAMPLE OF INPUT
var windowWidth = 1400; // read it...
var blockWidth = 200; // read it or use const...
var numberOfBlocks = 10; // read it...
// Calculate the maximum number of blocks per row
var maxBlocksPerRow;
for (var i=0; i < numberOfBlocks; i++) {
if ( (blockWidth * (i + 1)) > windowWidth){
maxBlocksPerRow = i;
break;
}
}
// Check the highest 0 module combination while iterating backwards
var magicNumberForMatchingBlocks = 1; // if not found, it will be 1.
for (var i = maxBlocksPerRow; i > 0 ; i--) {
if ( (numberOfBlocks % i) == 0){
magicNumberForMatchingBlocks = i;
break;
}
}
alert("With " + numberOfBlocks + " blocks, each one wide " +
blockWidth + " pixels, and a window wide " + windowWidth + " pixels,
the number of blocks per row for having always
the same number of block in any row is: " + magicNumberForMatchingBlocks);
Then use that number to populate or re-arrange the elements with Javascript or better with some Javascript library like jQuery.
html:
<div class="container">
<div class="grouped">
<div class="containee"></div>
<div class="containee"></div>
</div>
<div class="grouped">
<div class="containee"></div>
<div class="containee"></div>
</div>
</div>
css:
.containee {
width:200px;
height:200px;
display:inline-block;
background-color:tomato
}
.grouped {
float:left;
}
Try this:
.container
{
min-width: 410px;
}
Give the .containee a float:left; if the page fits for 4, they will be positioned right beside each other, else, you'll have another line of divs. You can give it as well a margin-top:5px; in case you got another line, the divs of the second line won't be glued to the divs of the first line. Note that with this approach, its not obliged to have equal number of .containee in each line, if you have 4, then you re-size, you'll have 3 - 1, then 2 - 2...etc..

Resources