I have a fixed-size div in which I am positioning several inline a's using the flexbox layout. My problem is that I also need to respond to clicks on the div, outside the text of the anchors, but flexbox is forcing their display to block so they take up all the available space, leaving no room for the user to click on the background div.
HTML:
<div class="poem">
<a class="title">Title</a>
<a>1919</a>
<a>Lead</a>
<a>Bachus</a>
</div>
Compiled CSS (I'm using SASS):
.title {
font-size: 1.85em;
}
.poem {
width: 12.5em;
height: 12.5em;
padding: .0625;
background-color: rgba(gray, 0.5);
display: inline-flex;
flex-direction: column;
}
.poem > a {
flex: 0 0 auto;
}
.poem > a:first-child {
flex: 1 1 auto;
}
.poem > a:first-child:hover {
background-color: rgba(255, 165, 0, 0.5);
}
This pen shows how I want to lay out the elements, and here is how they should actually work when hovered/clicked.
My question is whether there is any way to stop flexbox from forcing the elements' display to block and keep them inline or, alternatively, to detect whether a click was made specifically on an element's text.
I realize I could probably wrap each a in a div, but that would be non-semantic, so I'd rather leave it as a last resort.
Thank you.
.poem {
align-items: flex-start;
}
.poem > a:first-child {
flex: 0 1 auto;
margin-bottom: auto;
}
http://codepen.io/anon/pen/yGArn
Related
Please note that it's not a simple "how do I get XXX" question. I can achieve the look I'm going for but I'm surprised on how. Hence, I fear that I'm doing it in a bad way.
As shown in this fiddle, the text inside the divs is left aligned and the CSS text-adjust isn't effective. I suspect it's because the width of the div somehow is void, because setting the width to e.g. 15% for each numeric cell causes the expected behavior.
Of course, setting the width collides with the very point of enjoying flex, so that's not the right way to go. I'm reaching the requested look by applying justify-content but my understanding is that controlling the alignment that way (from the container div imposing placement on the child element) is supposed to be applied on blocks (i.e. div'ish not span'ish stuff). Am I confused/mistaken in this regard?
I've googled it but drowned in gazillions of posts on how to align children in a flex container. The closest relative to my issue is here but I don't really understand how it differs from what I'm trying to achieve. Also, it doesn't give me understanding of where my thinking went wrong (undoubtedly it did but I expected it not to).
Is it recommended to always have a non-flex'ish div inside the cell'ish div to encapsulate the text mass inside it? It seems like bad HTML markup.
div.data-row-cell {
display: flex;
padding: 3px;
}
div.data-row-value {
text-align: right;
flex: 1;
}
<div class="data-row">
<div *ngFor="let data of data"
class="data-row-cell data-row-value">
{{data}}
</div>
</div>
In order to understand how flex works, we should do some testing. Therefore I will build some example.
First its important to know that the default behaviour of a flex container direction is set to row. That means that all child elements inside a flex container will be placed next to each other as long as possible.
Also we don't think in left or right anymore when using flexbox. We no think in main axis and cross axis.
The main axis is the direction, the child elements are layed out. So per default it would be from left to right.
The cross axis would then be from top to bottom.
Next it is important to know, that by default the child elements inside the flex container only take as much space as needed.
/* just for some nice looking */
* {
font-family: sans-serif;
box-sizing: border-box;
}
.flex-container {
display: flex;
width: 100%;
padding: 1rem;
background: #666;
}
.flex-item {
padding: 0.5rem;
font-weight: bold;
color: white;
}
.r {
background: red;
}
.g {
background: darkgreen;
}
.b {
background: blue;
}
<div class="flex-container">
<div class="flex-item r">blue</div>
<div class="flex-item g">red</div>
<div class="flex-item b">green</div>
</div>
Since now we know the default behaviour of the flex container and the child elements, lets see what we have to do to make text-alignment work.
One way could be to stretch the child elements, so that the text inside has enough space for alignment.
We could do this with the property flex-grow for the child elements:
/* just for some nice looking */
* {
font-family: sans-serif;
box-sizing: border-box;
}
.flex-container {
display: flex;
width: 100%;
padding: 1rem;
background: #666;
}
.flex-item {
padding: 0.5rem;
font-weight: bold;
color: white;
}
.r {
background: red;
}
.g {
background: darkgreen;
}
.b {
background: blue;
}
/* Updated content */
.flex-item {
flex-grow: 1;
}
.r {
text-align: left;
}
.g {
text-align: center;
}
.b {
text-align: right;
}
<div class="flex-container">
<div class="flex-item r">blue</div>
<div class="flex-item g">red</div>
<div class="flex-item b">green</div>
</div>
So in your case, we could remove the display: flex from the .data-row-cell class and just add some flex-grow: 1
Please see my updated fiddle:
https://jsfiddle.net/7wfm1s0j/
I hope it helps :-)
Normally a when you call the parent display: flex then his children contain a default behavior that means it's default value get flex-direction: row. in a flexbox row element not work text-align property, it's work when you call the flex-direction: column here the solve your problem some example:
**your code**
div.data-row-value {
text-align: right;
flex: 1;
border-color: lightblue;
}
/*my example 1 */
div.data-row-value {
/* text-align: center; */
flex: 1;
border-color: lightblue;
justify-content: flex-end;
}
/*my example 2 */
div.data-row-value {
text-align: right;
flex: 1;
border-color: lightblue;
flex-direction: column;
}
/*my example 3 */
<div *ngFor="let data of data" class="data-row-cell data-row-value">
<div style="width:100%">{{data}}</div>
</div>
This is my flexbox setup:
section[_v-f4d9afa6] { // parent
display: flex;
}
article[_v-e514def2] { // child
display: flex;
flex: 1 1 50%;
}
article header[_v-e514def2] { // child of child
background-color: #484a47;
padding: 5px 0;
border-radius: 3px 3px 0 0;
}
article section[_v-e514def2] { // child of child 2
margin: 10px;
flex: 1 1 50%;
overflow-y: scroll;
}
I get this:
What should I change so the dark gray child is on the top? (Like an OS window)?
JSFiddle: https://jsfiddle.net/qjog7tvu/3/
You need to add flex-direction: column; for article[_v-e514def2]
One thing you can do is remove display: flex from article and just let it default as article and section are block element so header will arrange itself to top of section.
jsfiddle
Can i get the height of the previous element using only CSS?
I am using calc() function to set dynamically height of the div B.
#b{
height:calc(100vh - heightOfPreviousElement);
}
I need to know the height of the previous element.
what i know is that, 100vh is equal to 100% of the screen height.
I used the code in the answer below.Using flex,
I have one problem. The height of the color orange become smaller.
You can easily achieve the effect you're looking for using flexbox. The trick is to allow the blue container (the one with the flexible height) to grow in size whenever the need arises, using flex: 1 1 auto, which is simply a shorthand for:
flex-grow: 1;
flex-shrink: 1;
flex-basis: auto;
See proof-of-concept code snippet below:
body {
padding: 0;
margin: 0;
}
.wrapper {
display: flex;
flex-direction: column;
flex-wrap: no-wrap;
align-items: center;
justify-content: center;
height: 100vh;
}
.wrapper > div {
width: 100%;
}
#c1 {
background-color: #880015;
color: #fff;
height: 60px;
margin-bottom: 10px;
}
#c2 {
background-color: #ff7f27;
}
#c3 {
background-color: #00a2e8;
flex: 1 1 auto;
margin-bottom: 10px;
}
<div class="wrapper">
<div id="c1">height: 60px</div>
<div id="c2">height: auto (determined by content?)</div>
<div id="c3">flexible height</div>
</div>
No you can't select a previous element in CSS.
You might be interested in JQuery Prev OR Parents method for selecting previous element and apply height using .css() method?
Consider the following example where I have several inline-block elements in a container element that will potentially shrink in width to the point where the elements within it will wrap.
I would like for there to be a small bit of space between each "row" or contained elements. Using a margin-top or margin-bottom I get the space that I want. However, there is a small bit of space either on the first or last row of elements.
Is there a way to target the line wrapped elements but not the first line?
#container {
width: 10em;
background-color: blue;
}
#container > span {
display: inline-block;
padding: 0.25em;
margin: 0;
background-color: lightblue;
}
#container > span:not(:last-child) {
margin-right: 0.25em;
}
/*The following rule doesn't exist*/
#container > span:not(:first-row) {
margin-top: 0.25em;
}
<div id="container">
<span>some</span><span>text</span><span>of</span><span>some</span><span>elements</span><span>that</span><span>should</span><span>wrap</span>
</div>
Is there a way to target the line wrapped elements but not the first line?
No, not as of now.
However, there is a small bit of space either on the first or last row of elements.
In most situations, you should be able to mitigate that by a negative margin on the container element, like so:
#container {
width: 10em;
margin-bottom: -0.25em;
background-color: blue;
}
#container > span {
display: inline-block;
padding: 0.25em;
margin: 0 0 .25em 0;
background-color: lightblue;
}
#container > span:not(:last-child) {
margin-right: 0.25em;
}
.test { background:red; }
<div id="container">
<span>some</span><span>text</span><span>of</span><span>some</span><span>elements</span><span>that</span><span>should</span><span>wrap</span>
</div>
<div class="test">I am where I should be.</div>
(Added the red div to show that there’s no offset between it and the previous element.)
You can't target a bunch of spans as a row, especially if they're just wrapping when they don't fit on one line anymore.
You'd need to wrap the content in <div class="row"> for example. Then you could say .row:first-child { /* Custom styling */ }.
This might be possible in the future, but as it stands it can't be done.
I am faceing very strange behaviour of flexbox for my page layout.I am using FontAwesome to render certain symbols.
Basically, I have 2 flexbox items inside another flexbox item, and these child items render wider than parent item.
1) I have a .content__header object, which is rendered as a flexbox object inside another series of flexbox objects.
2)The .content__header object contains 2 child objects: .breadcrumb and .page_tools. These child objects are rendered as flex items as well.
3) Inside the .breadcrumb object, I have some span objects (.breadcrumb__test) who's content is replaced by a FontAwesome icon. The replacement is done using absolute positioning of the ::after pseudo-element.
4) When I remove all .breadcrumb__text HTML elements or just remove the .breadcrumb__text::after definition from my stylesheet - which defines the use of the FontAwesome font - the child objects (.breadcrumb and .page_tools) render at their correct width. So I guess it has something to do with the replacement by a FontAwesome icon.
.breadcrumb__text::after {
font-family: "FontAwesome";
font-weight: 400;
}
Visual representation of the issue
The green line indicates the difference between the parent width and it's actual contents.
Code & Fiddle below.
Browser: Google Chrome 47.0.2526.106 m
Fiddle:
https://jsfiddle.net/44gymmw2/6/
Update
When I remove text-indent: 100%; from the .breadcrumb__text CSS definition, the .breadcrumb renders as intended. However, when I leave the text-indent in place, and remove the .breadcrumb__text::after definition at the top of the style sheet (as described above), it also renders correctly.
Might this issue have something to do with the either FontAwesome or text-indent and flexbox?
Code
HTML
<body>
<div class="layout_wrapper">
<div class="layout_container__content">
<div class="content">
<header class="content__header">
<div class="breadcrumb">
<span class="breadcrumb__text breadcrumb__text--first">From </span><a class="breadcrumb__link" href="#">Dashboard</a><span class="breadcrumb__text"> to </span><a class="breadcrumb__link" href="#">Find records</a><span class="breadcrumb__text"> to </span>
<h1 class="breadcrumb__current">Kylo Ren’s Command Shuttle™</h1>
</div>
<ul class="page_tools">
<li class="page_tools__item">
<button type="button" class="button button--primary">Save</button>
</li>
<li class="page_tools__item">
<button type="button" class="button">Cancel</button>
</li>
</ul>
</header>
</div>
</div>
<div class="layout_container__sidebar">
<div class="sidebar">
<article class="widget">
<h2 class="widget__title">Widget</h2>
</article>
</div>
</div>
</div>
</body>
CSS
/* Removing this makes it work */
.breadcrumb__text::after {
font-family: "FontAwesome";
font-weight: 400;
}
/* Don't remove anything below */
.breadcrumb__text--first {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
* {
box-sizing: inherit;
}
html {
box-sizing: border-box;
}
html, body {
padding: 0;
margin: 0;
}
body {
display: flex;
flex-flow: column nowrap;
align-items: stretch;
}
.layout_wrapper {
flex: 1 1 auto;
display: flex;
flex-flow: row nowrap;
align-items: stretch;
}
.layout_container__content {
flex: 0 0 75vw;
}
.layout_container__sidebar {
flex: 0 0 25vw;
}
.content {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
align-items: stretch;
}
.content__header {
outline: 1px solid blue;
background-color: #434649;
color: #ffffff;
flex: 0 0 100%;
display: flex;
flex-flow: row nowrap;
}
.breadcrumb {
outline: 3px dashed purple;
flex: 1 1 auto;
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
}
.breadcrumb__text {
flex: 0 0 2em;
overflow: hidden;
text-indent: 100%;
white-space: nowrap;
position: relative;
text-align: center;
}
.breadcrumb__text::after {
content: '\f105';
padding: 0;
text-indent: 0;
position: absolute;
top: 0;
right: 0;
width: 100%;
}
.breadcrumb__text--first {
flex: 0 0 0;
}
.breadcrumb__text--first::after {
content: none;
}
.breadcrumb__link {
flex: 0 0 auto;
display: inline-block;
font-size: 0.8571rem;
}
.breadcrumb__current {
flex: 0 0 100%;
margin: 0;
}
.page_tools {
outline: 3px dashed red;
flex: 0 0 auto;
display: flex;
flex-flow: row nowrap;
list-style: none outside none;
}
I just finished reviewing your code and noticed the following:
There are unnecessary (nested) flex containers (caused by using display: flex;).
There are unnecessary flex-related declarations (just declaring the default behavior, such as flex-flow: row no-wrap; and align-items: stretch).
There are several unnecessary declarations in the context of various unrelated selectors. Probably left-over declarations while testing / debugging.
That said, lets continue to your issue. It isn't obvious, so it needs some introduction.
Pangloss introduces an important ingredient of your issue, which affects using text-indent. From W3:
The box is indented with respect to the left (or right, for right-to-left layout) edge of the line box. User agents must render this indentation as blank space.
[...]
If the value of text-indent is either negative or exceeds the width of the block, that first box, described above, can overflow the block. The value of overflow will affect whether such text that overflows the block is visible.
In other words: text-indent affects the dimensions of the box you apply it to.
You have declared overflow: hidden; on .breadcrumb__text. Obviously, for that declaration to have the desired effect, the box you applied it to needs a width, or else it wouldn't have a clipping edge.
.breadcrumb__text should get its width from the flex-basis declaration (specifically flex: 0 0 2em;) applied to it. Well... that's not happening, at least, not entirely as you would expect. Even though its width seems to be 2em, for some reason it doesn't trigger the overflow behavior as it should. This seems to be a bug in the specific Chrome version you use, as its fixed in Canary (eg. version 49 and up).
So: this seems to be an issue with the flexbox implementation in Chrome. That said, with this knowledge, your issue can be fixed in multiple ways. Some examples:
text-indent: -100%
Using text-indent: -100% fixes your issue because it takes away the extra whitespace on the right side of the affected elements. (→ jsfiddle)
width: 2em
You could add the declaration width: 2em to .breadcrumb__text. That would fix the unexpected behavior of the flex-basis declaration. (→ jsfiddle)
overflow: hidden; on parent container
Adding overflow: hidden; on .breadcrumbs fixes your issue. Now the parent container has a clipping edge and handles the whitespace caused by using text-indent: 100%. (→ jsfiddle)
Final remarks
Flexbox is a powerful layout mode and it sure is great to experiment with. But, the algorithms are complex and browser implementations aren't free from issues yet. Make sure you take this into account when using flexbox.
Another concern when looking at your code is the way you use flexbox. Flexbox is there for you to use, but it doesn't necessarily have to replace every other way of dealing with layout. display: inline-block; or display: table;, or even float might do the job without introducing the complexity of nested flex containers.
The FontAwesome typeface isn't the root cause. It's the H1 "block" layout definition set by default in your browser. Add this style to your breadcrumb__current class. See https://jsfiddle.net/44gymmw2/9/
word-wrap:break-word
The H1 tag is a block level element, which will make it push the contents of that block across the entire page. One other fix would be to change that from block to inline, like so:
h1 { display: inline; }
Or you could wrap that text in another element, like a span.
I think the problem is caused by the text-indent:100% rule, that added extra space on the right hand.
The text-indent property specifies how much horizontal space should
be left before the beginning of the first line of the text content of
an element. <percentage> value relative to the containing block
width. -MDN
See this simple demo:
body {
width: 200px;
border: 1px solid red;
}
span {
text-indent: 100%;
display: inline-block;
}
<span>text</span>
In your example, you can just change text-indent:100% to text-indent:-9999px, it works similarly, and it won't create the overflow.
Updated jsfiddle
Try witn inline-block property it is working good.
.content {
display:inline-block;
flex-flow: row wrap;
justify-content: space-between;
align-items: stretch;
}