Inline-block parent wrapping contents with height explicitly set to 0 - css

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 />

Related

how to get width of an element dynamically in reactjs?

I have this span element. am fetching data and putting some text in that span element, therefore sometimes that span elements width is 200 px, sometimes its 100px. I want this span to have margin-right: half of its width. I am using this technique:
const [width, setWidth] = useState()
const ref = useRef(null)
useLayoutEffect(()=>{
setWidth(ref.current.offsetWidth);
},[])
<span className='big ' id='meria' ref={ref} style={{marginRight: width / 2 }}>sometext</span>
I want the width element to re-render on change of window.location.pathname, but I cant use that as dependency.
any tips?
Don't use a dependency array. The useEffect would be called on each render, but if the offsetWidth didn't change, setting the state won't have any effect:
useLayoutEffect(() => {
setWidth(ref.current.offsetWidth);
})
Since the change happens on each render, you can skip the state, and just calculate it directly from the ref as suggested by kind user's comment:
<span className='big ' id='meria' ref={ref} style={{marginRight: ref.current.offsetWidth / 2 }}>sometext</span>
Note: margin doesn't work on inline elements, you should change the display CSS property inline-block or block.
Another option for this specific case is to set the margin using percentage in CSS, since according to MDN:
The size of the margin as a percentage, relative to the inline size
(width in a horizontal language, defined by writing-mode) of the
containing block.
Do you use a seperate CSS-stylesheet? (even if you don't this still should work, because it's basically just CSS) - If yes you can easily do some CSS trickery to get the same effect without a single line of JS needed You would do that as follows:
Wrap the span (or whatever element you want to have the dynamic margin for) in a div - this div then gets the width: fit-content - you can now set margin-right: 50% on your span element. This is everything you need for your desired result (The 50% always are calculated from the parent ... and with fit-content the parent will be the childs width)
Since you are using spans you'll need to add white-space: nowrap to the span (otherwise the span wouldn't overflow out of the div and just linebreak, which is not what we want)
span {
margin-left: 50%;
width: fit-content;
white-space: nowrap;
border: solid
}
.container {
width: fit-content;
}
<div class="container">
<span>short one</span>
</div>
<div class="container">
<span>this one is longer</span>
</div>
<div class="container">
<span>and this one is even longer than the one before</span>
</div>
I used margin-left for demonstartion purpouse (just change it tho whatever you need . the snippet is more to show what I meant, and show the dinamically changing maring based on width of the span)

Block not expanding vertically for inline padding

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.

Why does this input range push content downwards?

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.

Div width doesn't increase with it's content

In my application I have tags that can be from 5 to 15 characters. By that reason the tags width differ, but the surrounding divs increases with the parents width, not the content.
What should I put in the CSS to make the divs width adapt to the width of it's content?
Thanks in advance!
HTML
<div class="tag">
<a href="#">
<span class="content">Test album</span>
</a>
X
</div>
CSS
div.tag {
background: red;
}
Test case: http://jsfiddle.net/T4XJ3/1/
The <div> element has display:block, so it will always take the full width of their container.
You can make them "flexible" by using display: inline-block (demo).
Is this what you're looking for?
inline-block to the rescue!
div.tag {
background: red;
display: inline-block;
}
From the w3c spec:
This value causes an element to generate an inline-level
block container. The inside of an inline-block is formatted as a
block box, and the element itself is formatted as an atomic
inline-level box.
In simpler terms this means that outside of your div it acts like a span would (sizes to fit contents, flows inline in content, etc.), and inside of your div it acts like a div normally would (for positioning, sizing, padding, etc.).

Set percentage width for span element

A straight forward question.. is it possible to set the width in percentage for a span tag in CSS? for example:
<span style="width: 50%">...</span>
etc..
In my project I'm currently using divs but ofcourse after each div tag a line break gets inserted (which I don't want). So the most obvious solution to that is then to use span tags instead of div. But then I'm not able to define the width for the span tags.. Atleast not in a percentage kind of way.
Thanks in advance for any help!
Define the element as an inline block and you can control the width and height like a block element while keeping it inline with surrounding content.
#element {
display: inline-block;
width: 50%;
}
inline elements cannot have dimensions. do them to do so, and still remain inline, add:
display: inline-block
Add display: flex; on parent div style.
<div style="display: flex;">
<span style="width:50%">...</span>
<span style="width:50%">...</span>
</div>

Resources