I'm trying to align a vertical range input beside a column of labels. The input will have steps so the little bubble will always sit beside a label, and I have gotten the heights matching, so that's no problem.
However, no matter how I structure the two boxes (the input vertical range on the left and the labels on the right), the input always pushes the first label down, almost like it was a display: inline-block element.
HTML:
<div class="cell-left">
<input type="range" orient="vertical" min="0" max="3" step="1" />
</div>
<div class="cell-right">
<div class="row">Hello</div>
<div class="row">Hello</div>
<div class="row">Hello</div>
<div class="row">Hello</div>
</div>
CSS:
input[type=range] {
writing-mode: bt-lr; /* IE */
-webkit-appearance: slider-vertical; /* WebKit */
width: 8px;
height: 4rem;
padding: 0 5px;
}
.cell-left {
display: table-cell;
}
.cell-right {
display: table-cell;
}
https://jsfiddle.net/byn4kv0z/
That is expected behaviour. The vertical-align property by default is set to baseline which leads to the result. Changing the vertical-align property to top would give you the result you are looking for.
.cell-right {
display: table-cell;
vertical-align:top;
}
jsFiddle
Vertical-align can be quite tricky at times and there's quite a lot of magic going on in there. Here's a nice detailed article breaking it down into understandable terms: Vertical-align: All you need to know
Short answer
That's expected behavior of vertical-align: baseline (the implicit default value). You can use:
.cell-right {
display: table-cell;
vertical-align: top;
}
Fiddle
Explanation
Both the range input and the second table cell's text are part of the same inline formatting context. Regarding the default vertical-align value (baseline), the spec says:
baseline
Align the baseline of the box with the baseline of the parent box. If the box does not have a baseline, align the bottom margin edge with the parent's baseline.
This means the range input's bottom is aligned with the parent box's baseline, and so is the second cell's first line box's baseline. Here you can see where parent box's baseline is:
That is because the vertical range input stretches the containing line box, resulting in the effect you see.
As for your remark:
the input always pushes the first label down, almost like it was a display: inline-block element.
The range input is a replaced element, and its behavior is indeed nearly identical to an inline-block element.
Related
Please take a look on the following code:
.header {
display: flex;
width: 100vw;
height: 20vh;
}
div {
border: solid;
}
#first {
flex: 1
}
#second {
flex: 1
}
#third {
flex: 1
}
<header class="header">
<div id="first"></div>
<div id="second"></div>
<div id="third"></div>
</header>
This code gives three rectangles that lie one next to the other.
However, if you inspect each rectangle, you'll see in dev-tools
that these rectangles has display: block.
However, I know that when an element has a display: block property, it means that the element starts a new line. As you can see, it's not the case. All three rectangles are placed in the same line.
How can you explain this?
However, I know that when an element has a display: block property, it means that the element starts a new line.
Your information is wrong because the display property alone never tells us if we will have a new line or not.
Let's take another example without flexbox:
.box {
width:200px;
height:200px;
border:2px solid;
float:left;
}
<div class="box">
</div>
<div class="box">
</div>
You can clearly notice that the elements have the computed value of display equal to block but they don't start on a new line due to the float property. Same thing happen with flexbox, CSS grid, position:absolute and many others combination of properties.
All this is defined in the specification.
For float elements: https://www.w3.org/TR/CSS21/visuren.html#floats
If the current box is left-floating, and there are any left-floating boxes generated by elements earlier in the source document, then for each such earlier box, either the left outer edge of the current box must be to the right of the right outer edge of the earlier box, or its top must be lower than the bottom of the earlier box. Analogous rules hold for right-floating boxes.
For flexbox: https://www.w3.org/TR/css-flexbox-1/
The display value of a flex item is blockified: ...
The flex-direction property specifies how flex items are placed in the flex container, by setting the direction of the flex container’s main axis. This determines the direction in which flex items are laid out.
The default direction is row
You simply need to find the part of the specification dealing with the properties you are using to find how your elements will be placed in the document and you will clearly see that display alone isn't enough to define this.
Hi it's because your header display flex. If it displays flex, its all children will display in a line. You can change by add flex-direction: column or row (as default)
Your container div is set to be displayed as flex - so all of its children will inherit the display mode from the parent (and thus, display inline). You can set how they're displayed by specifying the flex-direction. Setting it to display as column will make all of the child elements display under one another.
.header {
display: flex;
width: 100vw;
height: 20vh;
/* Add this */
flex-direction:column;
}
div {
border: solid;
}
#first {
flex:1
}
#second {
flex:1
}
#third {
flex:1
}
<body>
<header class="header">
<div id="first"></div>
<div id="second"></div>
<div id="third"></div>
</header>
</body>
display: flex is tells your browser, "I wanna use flexbox with this container, please." A div element defaults to display:box.
An element with this display setting takes up the full width of the
line it is on. Here is an example of four colored divs in a parent
div with the default display setting:
JSFiddle demo
In a dropdown with a container element set to display: inline-block, there is a label (always visible, toggles dropdown overlay), and the overlay element itself. I am setting the overlay container to height: 0 and wishing to allow the overlay contents to exceed the height of the container, without affecting any parent elements. However, I am seeing some strange results - the overlay container is causing the parent of the dropdown to fully enclose the overlay contents too!
In the following HTML, ib = the inline block and h0 = the height:0 overlay container. See the jsfiddle demo to see it in action.
<div>
Sort by this
<span id="ib">
<span>LABEL</span>
<div id="h0">
DROPDOWN<br />
</div>
</span>
</div>
I don't wish to use position: absolute on the overlay, as I would like the contents of the overlay to drive the final width of the label. Surprisingly, I can achieve the desired outcome with the following css:
#ib { display: inline-flex; flex-direction: column; }
I'm happy to use that workaround for now, but also interested in the "why" behind this bizarre effect.
Your issue is about vertical-align rule for inline block elements. By default it baseline, here is some spec:
Baseline: Align the baseline of the box with the baseline of the parent box.
See also:
The height of each inline-level box in the line box is calculated. For replaced elements, inline-block elements, and inline-table elements, this is the height of their margin box; for inline boxes, this is their 'line-height'.
source: https://www.w3.org/TR/CSS2/visudet.html#line-height
and
CSS assumes that every font has font metrics that specify a characteristic height above the baseline and a depth below it. In this section we use A to mean that height (for a given font at a given size) and D the depth. We also define AD = A + D, the distance from the top to the bottom.
source: https://www.w3.org/TR/CSS2/visudet.html#inline-box-height
So your fix is ignore default inline-level block height setted via line-height and font-size by setting vertical-align: top/bottom/middle/text-top/text-bottom by your choice.
And fixed code:
.dropdown {
display: inline-block;
vertical-align: top;
}
.overlay {
height: 0;
}
<div>
sort by this
<span class="dropdown">
<span>LABEL</span>
<div class="overlay">
DROPDOWN<br />
DROPDOWN<br />
DROPDOWN
</div>
</span>
</div>
<hr />
I have a single-line fixed-width container div with two variable-width span inside. Any overflow of the first span should be hidden with an ellipsis. The second span floats on the right and should be shown in full. Please see this Fiddle:
<div class='container'>
<span class='left'>Long long variable stuff</span>
<span class='right'>Changing stuff</span>
</div>
I want the first span's width to dynamically adjust according to the width of the second span so that both span stay on the same line. How can I do that?
You can use Flexbox, so with flex: 1 on .right, .left will adjust its size and overflow will be hidden.
.container {
width: 200px;
display: flex;
border: 1px solid black;
}
.left {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.right {
flex: 1;
white-space: nowrap;
}
<div class='container'>
<span class='left'>Long long variable stuff</span>
<span class='right'>Changing stuff</span>
</div>
I don't think there's any way for CSS to dynamically know the length of an element without javascript. If you're looking for a purely CSS solution you're going to need to give it some direction in order for it to know the widths you want. Visually, that might be a bit of a compromise for you, but it will allow you to ensure that everything is always on one line.
For the solution I'm about to propose to work you need to know one width of the two. In this case I'm going to say that you can make a best guess of the "changing stuff" on the right.
Your first problem is that spans are inline elements by default - not inline-block. In order to get the overflow text property to work, you need to use it with an inline-block or block element.
The next piece is to use calc. Calc excepts mixed measurements so you can subtract an exact pixel value off of a percent. This works really well for responsive layouts.
I've created an updated version of your plunker to illustrate:
https://jsfiddle.net/n19ddahb/5/
A simple block element won't expand vertically unless its contents are of inline-block or block.
See fiddle: https://jsfiddle.net/4148xjvv/7/
Or see code:
HTML:
<div class='parent'>
<span class='padding'>Inline</span>
</div>
<br><br>
<div class='parent'>
<span class='inline-block padding'>Inline-block</span>
</div>
<br><br>
<div class='parent'>
<div class='padding'>Block</div>
</div>
CSS:
.parent {
background-color: red;
color: white;
}
.padding {
padding: 10px;
}
.inline-block {
display: inline-block;
}
Result:
The lateral padding works, but the vertical does not.
Chrome debugger shows that the padding is there, but bleeds out of the parent.
Obviously this isn't a huge issue, I can just change the children to inline-block if I need padding, so I want to know why this is happening.
I found this article to be very helpful in understanding what is happening: https://hacks.mozilla.org/2015/03/understanding-inline-box-model/
Any vertical padding, border, or margin applied to an element will not push away elements above or below it.
Basically, as you can see from the image above, the padding is added, it just doesn't change the vertical position of the element.
you are adding the padding to a span element, which is an inline element, where vertical padding won't move the element - its baseline (and therefore the text) stays where it is, due to the inline property of the span element.
therefore the vertical padding can only work in conjuction with the inline-block setting in your second parent element - or in your third parent element where you add it to a div element.
I ran into a small problem with floats that i demonstrate in this fiddle.
I have a DIV which floats to the left, whose width is dynamic (unknown). I have another one that floats to the right in the same block, width dynamic as well.
The problem is that if the width of the first block extends so that it would collide with the right float, the right float will (correctly) drop downwards to make sure no collision is happening. However, i want it to stay on top (vertically, that is - not in terms of z-index).
Basically it seems that the text is prioritized as to "displace" the block on the right side. This should be the other way around, but with the text on the left using up the available space on the topmost line before it even starts to wrap.
I guess the solution is fairly simple. Its just that it doesn't come to my mind at all and any searches i did didn't find me what i was looking for.
You might want to try using css tables/ Just create both elements and make it a table, then make your right and left elements table-cells:
#wrapper {
display: table;
width: 100%;
}
#leftside, #rightside {
display: table-cell;
width: 50%; /* Both sides will be rendered on one line */
vertical-align: top;
}
/* Position elements within the cell */
#leftside { text-align: left; }
#rightside { text-align: right; }
#leftside > div, #rightside > div {
display: inline-block;
text-align: left; /* Reset text alignment */
}
Explanation: The table structure will keep the elements in one line with width 50%; The inner elements (divs in this case) will be inline-blocks so that they can be aligned left or right. Now when one of the inner divs exceeds the max width of 50% it will just make the other 'cell' side smaller
Float the label div inside the title div, that will wrap the title text around the label regardless of the width of either.
<div class="infoBox">
<div class="inner">
<div class="entry">
<div class="title">
<div class="type">
LABEL
</div>
If this text is longer, the LABEL will drop downwards.
I would like to have the LABEL float right (as it does here) but also be at the top of the block.
</div>
</div>
</div>
​