How to vertically center a floating element of unknown height in Bootstrap? - html

Related Bootply: http://www.bootply.com/OdVIzrxTV5
I am creating a layout in Bootstrap where sections stack atop one another on mobile:
|---|
| A |
|---|
| B |
|---|
| C |
|---|
but split apart on desktop:
|---| |---|
| B | | A |
|---| |---|
| C |
|---|
I am using .pull-right on sections A and C to do the job, but because Bootstrap is using float, section B is automatically aligned to the top as seen above. What I really want is for section B to be vertically centered like this:
|---|
|---| | A |
| B | |---|
|---| | C |
|---|
Section B should be vertically centered with respect to A and C. I've tried applying flexbox on the row, but that disrupts the layout. I've also tried various other CSS hacks but none seemed to work correctly in a responsive manner, disrupting the layout on either mobile or desktop.
Is there any reliable, responsive solution to vertically center section B when it is of unknown height? If it helps, section B is going to be an image element.

What other CSS hacks have you tried? Just curious as I know when working the factory version of Bootstrap (no sass/less) you really do have to be picky with the CSS selectors.
That being said, perhaps a JavaScript approach might help! Here's the code, I'll walk you through some changes you could make to suit your needs.
Variable breakpoint
Change this variable to whatever pixels are set for your breakpoint. For Bootstrap the xs selector kicks in at 768 by default. You could add multiple breakpoints and use more if/else statements if you wanted to do things at other breakpoints.
First if
This will trigger when the page loads and will immediately test what the width of your screen is and change the margin if need be.
window.resize()
Anytime the window is resized a similar if statement goes off and re-calculates the margin. If you throw some text in and change the height, you'll see that it stays centred. If for some reason the browser is resized below the breakpoint, the margin is simply changed to inherit.
Some Changes
You might notice that the div is a little lower than half. This is probably just a math error on how jQuery is retrieving the height. Simply add whatever constant you want to edit it up or down.
// Where 20 is in pixels
var topMargin = (totalMargin / 2) - 20;
If you wanted to center the left div so it's always in-between sections A and C you would just have to change the topMargin calculation to use the height of the A div.
If you have any questions let me know!
$(document).ready(function() {
var breakpoint = 768;
if ($(window).width() > breakpoint) {
var totalMargin = $(window).height() - $('#vertCenter').height();
var topMargin = totalMargin / 2;
$('#vertCenter').css('margin-top', topMargin);
}
$(window).resize(function(){
if ($(window).width() > breakpoint) {
var totalMargin = $(window).height() - $('#vertCenter').height();
var topMargin = totalMargin / 2;
$('#vertCenter').css('margin-top', topMargin);
} else {
$('#vertCenter').css('margin-top', 'inherit');
}
});
});
.section-a,
.section-c {
font-size: 2em;
}
.section-b {
font-size: 4em;
}
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<div class="container">
<div class="row">
<div class="test"></div>
<div class="col-xs-12 col-sm-6 pull-right">
<div class="section-a well well-lg">A</div>
</div>
<div class="col-xs-12 col-sm-6">
<div id="vertCenter" class="section-b well well-lg">B</div>
</div>
<div class="col-xs-12 col-sm-6 pull-right">
<div class="section-c well well-lg">C</div>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

Related

Responsive image and text

I'm trying to create a responsive <div> with a maximally square svg image in it, and some text next to it. What I have so far is (with the help of answers to several Stackoverflow questions asked by others):
<div class = 'outer'>
<div class = 'inner'>
<svg width = '100%' height = '100%' viewBox = '-35 -35 70 70'
preserveAspectRatio = 'xMaxYMin meet' id = 'yadayadayada'
xmlns = "..." version = "1.1" xmlns:xlink = "..." xmlns:svgjs = "...">...</svg>
</div>
</div>
where the svg image is generated by SVG.js:
let image = SVG () . size ('100%', '100%')
. addTo ('.inner')
. id ('yadayadayada')
. viewBox (-35, -35, 70, 70)
. attr ({preserveAspectRatio: 'xMaxYMin meet'});
//
// SVG drawing stuff
//
With the following CSS:
div.outer::before, div.outer::after {
height: 5%;
display: block;
content: "";
margin: 0 0 0 0;
}
div.outer {
margin: 0 0 0 0;
padding-left: 5%;
padding-right: 5%;
width: 90%;
height: 90%;
}
div.inner {
width: 100%;
height: 100%;
position: relative;
}
This creates a view like this:
+------------+---------------+
| | |
| | |
| | |
| Blank | SVG |
| | |
| | |
| | |
+------------+---------------+
That is, a <div> filling the entire window, with a 5% margin on each side, and a square SVG image on its right hand side (or left hand side if I change the preserveAspectRatio to xMinYMin meet). This scales as I want when changing the size of the window. So far, so good.
But now, I want to have some text next to the image (either right or left, depending on where the image is located), with the image still fully visible, and the same size. And that's where I get stuck. It's easy to get some text there using some absolute positioning, but if I add some text inside the inner div, this has the effect of shifting down the image, so the top of the image is below the bottom of the text (even if the text is a single line, shorter than the width of the white space).
I think what is happening is that the <svg> isn't actually square, but has the same size as the inner div, making that it gets pushed out of that div if text is getting added.
Can someone suggest what to do, so I can have a maximally sized square image, with text next to it, and things still being responsive? I don't really care what happens if the window gets resized to nearly square, or to portrait mode. The intended purpose is for slides for a presentation, where I still want to have the slides look good if I switch the browser to/from full screen, remove toolbars, or connect my laptop to a beamer -- in all cases, the window will be in landscape mode, and not close to being a square.
I'm trying to create a responsive <div> with a maximally square svg
image in it, and some text next to it.
Do you want to put your text where blank is currently?
If so, it sounds like your best approach will be CSS Grid.
Start by introducing an extra <div> for the text you want to add:
<div class = "outer">
<div class = "textbox">
</div>
<div class = "inner">
[... CONTENTS OF .inner HERE...]
</div>
</div>
Then give .outer:
a display value of grid
one row
two equal columns
with the following CSS:
.outer {
display: grid;
grid-template-rows: 45vw;
grid-template-columns: 45vw 45vw;
}
This will automatically give:
.textbox a width of 45 viewport width units (ie. 45% the width of the viewport)
.inner a width of 45 viewport width units

CSS Fixed Header, scrolling body with left sidbar/content/right sidbar and footer

I have spent the last week trying to tweak a layout and keep rebuilding from scratch without success, so asking for help.
I'm trying to achieve
1. a fixed header which does not scroll and always viewable
2. a main area with left sidebar (nav) / content / right sidebar (ads) which scroll
3. a footer which is only visible when scrolled to the bottom
My vision is :
|--------------------------------|
| header (always viewable ) |
|--------------------------------|
|| L | | R ||
|| e | | i ||
|| f | | g ||
|| t | | h ||
|| | | t ||
|| S | Content Area | ||
|| i | | S ||
|| d | | i ||
|| e | | d ||
|| b | | e ||
|| a | | b ||
|| r | | a ||
|| | | r ||
||------------------------------||
|| footer visible at end scroll ||
||------------------------------||
The basic structure of the HTML has a few more divs than could be done with but I'm planning for maximum flexibility ( and problably creating problems)
<div class="site-container">
<div class="site-header">
SITE HEADER
<!-- HEADER CONTENT GOES HERE -->
</div> <!-- site-header -->
<div class="site-content">
SITE-CONTENT (with reduced padding not visible )
<div class="content-main">
<div class="content-main-left">
CONTENT-MAIN-LEFT ( Navigation )
</div>
<div class="content-main-middle">
CONTENT-MAIN
<!-- MIDDLE COLUMN CONTENT GOES HERE -->
Lorem ipsum dolor etc ...
</div>
<div class="content-main-right">
CONTENT-MAIN-RIGHT ( Ads )
</div>
<div class="content-footer">
CONTENT-FOOTER
</div>
</div> <!-- content-main -->
</div> <!-- site-content -->
</div> <!-- site-container -->
However, the more I play with it, the more messy and unworkable it gets.
I'm currently stuck in the loop of fixing one problem while creating another.
Here's my JSFiddle version
Any help is greatly appreciated.
Thanks
You'll want to work out how you want that central column of text to work.
If you have a fixed width container, you could float the elements, and give them a fixed width.
Many grid systems like Bootstrap or Base use floated columns to lay things out. They also use media queries in CSS to cater for different screen sizes.
If you want a layout that has fixed-width columns and a variable width central column, and you did want to do things yourself, you can position things absolutely inside your main content holder.
Check out an updated JSFiddle to see how you could use position:absolute to lay things out.
There's a few updates to your HTML in there, but a CSS layout like this:
.site-header {
position: fixed;
top:0px;
width: 100%;
height: 50px;
padding:20px 0;
z-index:2;
}
.content-main-left {
width:100px;
min-height:200px;
position:absolute;
left:0;
background-color: tan;
}
.content-main {
padding:0 120px 0 120px; /* padding to make room for the left and rigth columns */
}
.content-main-right {
width:100px;
min-height:200px;
position:absolute;
right:0;
top:0;
background-color: rosybrown;
}
.site-content {
position:relative;
top:120px;
}
.content-footer {
position: relative;
left:0;
height: 60px;
width: 100%;
}
It's always good to strip things back and get one thing working at a time. There were a few lines in that probably weren't helping:
The left column <div> wasn't closed, so your main content was sitting inside that <div>
It looks like you've tried to close the 'site-header' tag at the bottom of the content.
Putting a <div> around your left, center and right columns will help you place a footer at the bottom of all three sections. I've renamed this "site-footer"
Hope this helps!
I found, that if my left / right sidebars grow in length then the bottom of the page starts to get messy.
i.e. jsFiddle
I spent a lot of time playing around but couldn't get it working.
I did not know about the calc option, which I think enables me to have an easier to manage (for a CSS noob like me) solution as I can stay away from absolute positioning.
The result is jsFiddle.
I'd love the option of getting the left/right bars to grow to the bottom using something like
height: calc(100% - 100);
but it doesn't seem to work, and presumably I'd have to delve back into the world of absolute to get it to work.
Again, thanks for the help.

Set element at the vertical middle of a row

In my grid, I want to do something like this:
[main row]
|[columns-10] |[columns-2][row]"above img"|
| Some text | |
| here, on |[row] image here|
| multiple | |
| lines |[row] "below img"|
| ... | |
Notice how the rows in columns-2 are evenly spaced (vertically), relative to the text in columns-10.
Zurb gets me close but the rows inside the columns-2 seem to not be aware of the height of the outer row.
Here's my (haml) code:
.row
.columns.large-10
%p #{"It does work on all browsers " * 20 }
.columns.large-2
.row.right
.columns.large-12
above img
.row.right
.columns.large-12
%img{ src: "http://www.gravatar.com/avatar/4d1795fc22c16e533fd1d11fae39fef8.png",
alt: "Baboon's logo" }
.row.right
.columns.large-12
below img
Edit: Here's the JSFiddle try to resize the window and see how the image stays in the top left corner instead of being at mid height of the large text.
You need to include jQuery library in your RoR application and change the HAML code and add a JS file with the below JS code.
HAML Code:
.row
.columns.large-10
%p #{"It does work on all browsers " * 20 }
.columns.large-2
.row.right
.columns.large-12
above img
.row.right#image-container
.columns.large-12
%img{ src: "http://www.gravatar.com/avatar/4d1795fc22c16e533fd1d11fae39fef8.png",
alt: "Baboon's logo" }
.row.right
.columns.large-12
below img
JS code with jQuery library:
var height=$('#image-container').parent().siblings().height();
var above_height=$('#image-container').siblings().height();
$('#image-container').find('img').height(height-2*above_height)
Here is the JSFiddle demo.

How do I bottom-align grid elements in bootstrap fluid layout

I have a fluid layout using Twitter's bootstrap, wherein I have a row with two columns. The first column has a lot of content, which I want to fill the span normally. The second column just has a button and some text, which I want to bottom align relative to the cell in the first column.
Here's what I have:
-row-fluid-------------------------------------
+-span6----------+ +-span6----------+
| | |short content |
| content | +----------------+
| that |
| is tall |
| |
+----------------+
-----------------------------------------------
Here's what I want:
-row-fluid-------------------------------------
+-span6----------+
| |
| content |
| that |
| is tall | +-span6----------+
| | |short content |
+----------------+ +----------------+
-----------------------------------------------
I've seen solutions that make the first span an absolute height, and position the second span relative to it, but a solution where I didn't have to specify the absolute height of my divs would be preferred. I'm also open to a complete rethink of how to achieve the same effect. I'm not married to this use of the scaffolding, it just seemed to make the most sense to me.
This layout as a fiddle:
http://jsfiddle.net/ryansturmer/A7buv/3/
This is an updated solution for Bootstrap 3 (should work for older versions though) that uses CSS/LESS only:
http://jsfiddle.net/silb3r/0srp42pb/11/
You set the font-size to 0 on the row (otherwise you'll end up with a pesky space between columns), then remove the column floats, set display to inline-block, re-set their font-size, and then vertical-align can be set to anything you need.
No jQuery required.
Please note: for Bootstrap 4+ users, please consider Christophe's solution (Bootstrap 4 introduced flexbox, which provides for a more elegant CSS-only solution). The following will work for earlier versions of Bootstrap...
See http://jsfiddle.net/jhfrench/bAHfj/ for a working solution.
//for each element that is classed as 'pull-down', set its margin-top to the difference between its own height and the height of its parent
$('.pull-down').each(function() {
var $this = $(this);
$this.css('margin-top', $this.parent().height() - $this.height())
});
On the plus side:
in the spirit of Bootstrap's existing helper classes, I named the class pull-down.
only the element that is getting "pulled down" needs to be classed, so...
...it's reusable for different element types (div, span, section, p, etc)
it's fairly-well supported (all the major browsers support margin-top)
Now the bad news:
it requires jQuery
it's not, as-written, responsive (sorry)
You can use flex:
#media (min-width: 768px) {
.row-fluid {
display: flex;
align-items: flex-end;
}
}
You need to add some style for span6, smthg like that:
.row-fluid .span6 {
display: table-cell;
vertical-align: bottom;
float: none;
}
and this is your fiddle: http://jsfiddle.net/sgB3T/
Here's also an angularjs directive to implement this functionality
pullDown: function() {
return {
restrict: 'A',
link: function ($scope, iElement, iAttrs) {
var $parent = iElement.parent();
var $parentHeight = $parent.height();
var height = iElement.height();
iElement.css('margin-top', $parentHeight - height);
}
};
}
Just set the parent to display:flex; and the child to margin-top:auto. This will place the child content at the bottom of the parent element, assuming the parent element has a height greater than the child element.
There is no need to try and calculate a value for margin-top when you have a height on your parent element or another element greater than your child element of interest within your parent element.
This is based on cfx's solution, but rather than setting the font size to zero in the parent container to remove the inter-column spaces added because of the display: inline-block and having to reset them, I simply added
.row.row-align-bottom > div {
float: none;
display: inline-block;
vertical-align: bottom;
margin-right: -0.25em;
}
to the column divs to compensate.
Based on the other answers here is an even more responsive version. I made changes from Ivan's version to support viewports <768px wide and to better support slow window resizes.
!function ($) { //ensure $ always references jQuery
$(function () { //when dom has finished loading
//make top text appear aligned to bottom: http://stackoverflow.com/questions/13841387/how-do-i-bottom-align-grid-elements-in-bootstrap-fluid-layout
function fixHeader() {
//for each element that is classed as 'pull-down'
//reset margin-top for all pull down items
$('.pull-down').each(function () {
$(this).css('margin-top', 0);
});
//set its margin-top to the difference between its own height and the height of its parent
$('.pull-down').each(function () {
if ($(window).innerWidth() >= 768) {
$(this).css('margin-top', $(this).parent().height() - $(this).height());
}
});
}
$(window).resize(function () {
fixHeader();
});
fixHeader();
});
}(window.jQuery);
Well, I didn't like any of those answers, my solution of the same problem was to add this:<div> </div>. So in your scheme it would look like this (more or less), no style changes were necessary in my case:
-row-fluid-------------------------------------
+-span6----------+ +----span6----------+
| | | +---div---+ |
| content | | | & nbsp; | |
| that | | +---------+ |
| is tall | | +-----div--------+|
| | | |short content ||
| | | +----------------+|
+----------------+ +-------------------+
-----------------------------------------------
.align-bottom {
position: absolute;
bottom: 10px;
right: 10px;
}

Getting the larger of two divs floated side-by-side to extend its border?

I have two <div>s which look like this:
---------------------------
| | |
| DIV 1 | DIV 2 |
| | |
---------------------------
The first div is floated left, while the second div is floated right. The left div has a right border, while the second div has a left border.
I would like the larger of these divs to extend its border all the way to the bottom of their container and I want only one border.
How could I achieve this?
Any help would be greatly appreciated.
Use jQuery
add this in head
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script>
and put this snippet in head or body.
<script type="text/javascript">
$(document).ready(function() {
var large=$(div).eq(0), small = $(div).eq(1), temp;
if(large.height()<small.height()) {
temp = large;
large = small;
small = temp;
}
large.css('border-right','1px solid black');
small.css('border-left', '0px');
});
</script>
Do you mean that you want the larger div to be the full height of the container? If that's what you mean, you can specify the height to be 100% in the css for the larger div.
One way to achieve this effect is via absolute positioning. In this method, the width of your container is known (like 1024px for example) and you divide the available space between your two divs, 324px for the your div2 and 700px for div1. This way, you don't have to float divs. All you have to do is to define position: relative; for the container, and position: absolute; for two divs. Then you have to specify
top: 0;
right: 324px;
bottom: 0;
left: 0;
for your div1.
Another method is the floating that you've used here. By floating, you should float your both divs to the left or right, and specify width for each of them. Then you should not worry about the height of the width, because the div1 would be extended based on its contents.
if you mean that div one is higher then div 2 and you want them to be the same height then this is the script for you.
function setHeight(id1, id2){
//haal de elementen op en plaats deze in een lokale variabel
var elem1 = document.getElementById(id1);
var elem2 = document.getElementById(id2);
// controleer of de offsetHeight van element1 of 2 groter is
// dan de ander en pas de kleinste aan
if(elem1.offsetHeight>elem2.offsetHeight){
elem2.style.height = elem1.offsetHeight+'px';
}else{
elem1.style.height = elem2.offsetHeight+'px';
}
}
put this in a javascript file or in the head of your html inside de script tags ofcourse and put the following code inside your body tag
onload="setHeight(id1, id2)"
now you just need to give the div's you want to resize the id1 and 2(can be any name, as long as its is there in the parameters) and it works :)
good luck,
and let me know if you have isues. i use this myself so it works :)