Why is the descender “created” when baseline is set to vertical-align? - css

I know that the img element has a descender margin below it by default.
.box {
border: 1px solid;
}
<div class="box">
<img src="http://placehold.jp/150x150.png"> xyz
</div>
Question
I thought that the vertical-align property "aligns" elements at a specific position, so the height doesn't change. However, with the following code, when measuring the height when changing vertical-align, height was increased only forbaseline.
Why does it behave like "making a descender only when baseline"? Isn't a descender always present regardless of the value of the vertical-align property? There was no mention of this behavior in the vertical-align specification.
var align = ["top", "bottom", "baseline"];
var idx = 0;
function changeVerticalAlign() {
document.querySelector(".box>img").style.verticalAlign = align[idx];
document.querySelector("p").innerText = "height=" + getComputedStyle(document.querySelector(".box")).getPropertyValue("height") + ", vertical-align=" + getComputedStyle(document.querySelector(".box>img")).getPropertyValue("vertical-align");
idx < 2 ? ++idx : idx = 0;
}
document.querySelector("button").addEventListener("DOMContentLoaded", changeVerticalAlign);
document.querySelector("button").addEventListener("click", changeVerticalAlign);
.box {
border: 1px solid;
}
<div class="box">
<img src="http://placehold.jp/150x150.png"> xyz
</div>
<p></p>
<button>change vertical-align to "bottom"</button>

I thought that the vertical-align property "aligns" elements at a specific position, so the height doesn't change.
This isn't true because the height is defined after the alignment is done and you can clearly notice this if you use text instead of image:
.box{
border:1px solid;
margin:5px;
}
<div class="box">
<span>some text</span>
<span>some text</span>
<span>some text</span>
</div>
<div class="box">
<span style="vertical-align:super">some text</span>
<span>some text</span>
<span>some text</span>
</div>
<div class="box">
<span style="vertical-align:super">some text</span>
<span>some text</span>
<span style="vertical-align:sub">some text</span>
</div>
From the specification:
The boxes may be aligned vertically in different ways: their bottoms or tops may be aligned, or the baselines of text within them may be aligned. The rectangular area that contains the boxes that form a line is called a line box.
and also
A line box is always tall enough for all of the boxes it contains. However, it may be taller than the tallest box it contains (if, for example, boxes are aligned so that baselines line up). When the height of a box B is less than the height of the line box containing it, the vertical alignment of B within the line box is determined by the 'vertical-align' property.
Then in another part of the specification
As described in the section on inline formatting contexts, user agents flow inline-level boxes into a vertical stack of line boxes. The height of a line box is determined as follows:
The height of each inline-level box in the line box is calculated. ....
The inline-level boxes are aligned vertically according to their 'vertical-align' property. ....
The line box height is the distance between the uppermost box top and the lowermost box bottom. (This includes the strut, as explained under 'line-height' below.)
So basically the height is defined bigger enough to hold the content after being aligned that's why the height may increase for some alignment.
If you add border to your elements you will notice the uppermost box top and the lowermost box bottom.
var align = ["top", "bottom", "baseline","super","sub","text-bottom","text-top"];
var idx = 0;
function changeVerticalAlign() {
document.querySelector(".box>img").style.verticalAlign = align[idx];
document.querySelector("p").innerText = "height=" + getComputedStyle(document.querySelector(".box")).getPropertyValue("height") + ", vertical-align=" + getComputedStyle(document.querySelector(".box>img")).getPropertyValue("vertical-align");
idx < 6 ? ++idx : idx = 0;
}
document.querySelector("button").addEventListener("DOMContentLoaded", changeVerticalAlign);
document.querySelector("button").addEventListener("click", changeVerticalAlign);
.box {
border: 1px solid;
}
.box> * {
border:1px solid red;
}
p {
margin:0;
}
<div class="box">
<img src="http://placehold.jp/150x130.png"> <span>xyz</span>
</div>
<p></p>
<button>change vertical-align</button>

Related

When keep typing without any space in a textarea, the display from the textarea showing in a div will exceed the width of the div?

Why keep typing without any space in a textarea, the output from the textarea shown in a div will exceed the width of the div? But the issue won't happen when typing with spaces.
function grab(){
var x= document.getElementById("text").value;
document.getElementById("replace").innerHTML = x;
}
<body>
<form>
<textarea id="text" onKeyUp="grab();" onKeyPress="grab();"></textarea><br>
</form>
<div id="replace" style="height: 20px; width: 100px;">1</div><br>
</body>
As there is no spacing added between your words, so it overflows text outside div no line-break is added, so to solve that you could add CSS word-wrap property to break a long word and wrap that to next line as soon as it reaches end of div.
word-wrap -
The word-wrap property allows long words to be able to be broken and
wrap onto the next line.
function grab() {
var x = document.getElementById("text").value;
document.getElementById("replace").innerHTML = x;
}
var ta = document.querySelector("form > textarea");
ta.addEventListener("keyup", grab);
#replace {
width: 200px;
height: 100px;
border: solid;
word-wrap: break-word;
}
<form>
<textarea id="text"></textarea>
<br>
</form>
<div id="replace">1</div>
<br>

What's the deal with vertical-align: baseline?

I thought I knew my way around CSS, but I needed to explain something to someone just now and I found I couldn't.
My question basically boils down to: why is vertical-align:baseline ignored when there are other alignments in the same line?
Example: if the second span has vertical-align:bottom, the first span's vertical alignment is ignored if it is baseline; it behaves as if it has bottom too.
span:first-child {vertical-align:baseline}
span:last-child {font-size:3em; vertical-align:bottom;}
<p>
<span>one</span> <span>two</span>
</p>
While if all the spans have a vertical-align other than baseline, or, if they are all baseline, then they behave as expected.
span:first-child {vertical-align:top}
span:last-child {font-size:3em; vertical-align:bottom;}
<p>
<span>one</span> <span>two</span>
</p>
span:first-child {vertical-align:baseline}
span:last-child {font-size:3em; vertical-align:baseline;}
<p>
<span>one</span> <span>two</span>
</p>
If this is normal behaviour, then why isn't it described anywhere? I haven't found any source that says baseline and top/bottom interfere with each other in such a way.
Vertical-Align
vertical-align is used to align inline-level elements. These are elements, whose display property evaluates to:
inline
inline-block
inline-table (not considered in this answer)
Inline-level elements are laid out next to each other in lines. Once there are more elements than fit into the current line, a new line is created beneath it. All these lines have a so-called line box, which encloses all the content of its line. Differently sized content means line boxes of different height.
In the following illustration the top and bottom of line boxes are indicated by red lines.
Inside these line boxes the property vertical-align is responsible for aligning the individual elements.
Baseline
The most important reference point to align vertically is the baseline of the involved elements. In some cases the top and bottom edge of the element’s enclosing box becomes important, too.
Inline elements
The top and bottom edge of the line height is indicated by red lines, the height of the font by green lines and the baseline by a blue line.
On the left, the text has a line height set to the same height as the font-size. The green and red line collapsed to one line on each side.
In the middle, the line height is twice as large as the font-size.
On the right, the line height is half as large as the font-size.
Note that the inline element’s outer edges (the red lines) does not matter, if the line height is smaller than the height of the font.
Inline-Block Element
From left to right you see:
an inline-block element with in-flow content
an inline-block element with in-flow content and overflow: hidden
an inline-block element with no in-flow content (but the content area has a height)
The boundaries of the margin is indicated by red lines, the border is yellow, the padding green and the content area blue. The baseline of each inline-block element is shown as a blue line.
The inline-block element’s baseline depends on whether the element has in-flow content. In case of:
in-flow content the baseline of the inline-block element is the baseline of the last content element in normal flow (example on the left)
in-flow content but an overflow property evaluating to something other than visible, the baseline is the bottom edge of the margin-box (example in the middle)
no in-flow content the baseline is, again, the bottom edge of the margin-box (example on the right)
Line box
This is probably the most confusing part, when working with vertical-align. It means, the baseline is placed where ever it needs to be to fulfil all other conditions like vertical-align and minimizing the line box’s height. It is a free parameter in the equation.
Since the line box’s baseline is invisible, it may not immediately be obvious where it is. But, you can make it visible very easily. Just add a character at the beginning of the line in questions, like the "x" in the figure. If this character is not aligned in any way, it will sit on the baseline by default.
Around its baseline the line box has what we might call its text box (green lines in the figure). The text box can simply be thought of as an inline element inside the line box without any alignment. Its height is equal to the font-size of its parent element. Therefore, the text box only just encloses the unformatted text of the line box. Since this text box is tied to the baseline, it moves when the baseline moves.
Snippet
If you want to do some experiment with various vertical-align and font-size here you have a snippet where you can try it out. Is also available in JSFiddle.
let sl1 = document.getElementById('sl1');
let sl2 = document.getElementById('sl2');
let sl3 = document.getElementById('sl3');
let sl4 = document.getElementById('sl4');
let elm1 = document.getElementById('elm1');
let elm2 = document.getElementById('elm2');
let elm3 = document.getElementById('elm3');
let elm4 = document.getElementById('elm4');
let ip1 = document.getElementById('ip1');
let ip2 = document.getElementById('ip2');
let ip3 = document.getElementById('ip3');
let ip4 = document.getElementById('ip4');
let slArr = [sl1, sl2, sl3, sl4];
let elmArr = [elm1, elm2, elm3, elm4];
let ipArr = [ip1, ip2, ip3, ip4];
let valueArr = ['baseline', 'top', 'middle', 'bottom'];
for (let i = 0; i < slArr.length; i++) {
slArr[i].addEventListener('change', (event) => {
elmArr[i].style.verticalAlign = event.target.value;
elmArr[i].innerHTML = event.target.value;
addDiv();
})
}
for (let i = 0; i < ipArr.length; i++) {
ipArr[i].addEventListener('change', (event) => {
elmArr[i].style.fontSize = event.target.value + 'em';
addDiv();
})
}
document.getElementById('btnRandom').addEventListener('click', () => {
for (let i = 0; i < elmArr.length; i++) {
let element = elmArr[i];
let fontSize = Math.floor(Math.random() * 4 + 1);
ipArr[i].value = fontSize;
element.style.fontSize = fontSize + 'em';
let styleIndex = Math.floor(Math.random() * 4);
element.style.verticalAlign = valueArr[styleIndex];
element.innerHTML = valueArr[styleIndex];
slArr[i].selectedIndex = styleIndex;
}
}, this);
function addDiv() {
let view = document.getElementById('viewer');
view.innerHTML = "";
elmArr.forEach(function(element) {
let div = document.createElement('div');
div.appendChild(element.cloneNode());
view.appendChild(div);
}, this);
}
.header span {
color: #000;
}
select {
width: 100px;
}
#elms {
border: solid 1px #000;
margin-top: 20px;
position: relative;
}
span {
color: #FFF;
font-size: 1em;
}
#elm1 {
background-color: #300;
}
#elm2 {
background-color: #6B0;
}
#elm3 {
background-color: #90A;
}
#elm4 {
background-color: #B00;
}
div {
z-index: -1;
}
#div1 {
width: 100%;
height: 1px;
background-color: #000;
position: absolute;
left: 0;
top: 25%;
}
#div2 {
width: 100%;
height: 1px;
background-color: #000;
position: absolute;
left: 0;
top: 50%;
}
#div3 {
width: 100%;
height: 1px;
background-color: #000;
position: absolute;
left: 0;
top: 75%;
}
<div class="header"> <span style="width: 100px;display: block;float: left;margin-right: 20px;">vertical align</span> <span>font-size(em)</span> </div>
<div>
<select name="sl1" id="sl1">
<option value="baseline">baseline</option>
<option value="top">top</option>
<option value="middle">middle</option>
<option value="bottom">bottom</option>
</select>
<input type="number" value="1" id="ip1" />
<br>
<select name="sl2" id="sl2">
<option value="baseline">baseline</option>
<option value="top">top</option>
<option value="middle">middle</option>
<option value="bottom">bottom</option>
</select>
<input type="number" value="1" id="ip2" />
<br>
<select name="sl3" id="sl3">
<option value="baseline">baseline</option>
<option value="top">top</option>
<option value="middle">middle</option>
<option value="bottom">bottom</option>
</select>
<input type="number" value="1" id="ip3" />
<br>
<select name="sl4" id="sl4">
<option value="baseline">baseline</option>
<option value="top">top</option>
<option value="middle">middle</option>
<option value="bottom">bottom</option>
</select>
<input type="number" value="1" id="ip4" />
<br>
<button id="btnRandom" (onclick)="random()">Random</button>
</div>
<div id="elms">
<span id="elm1">one</span>
<span id="elm2">two</span>
<span id="elm3">three</span>
<span id="elm4">four</span>
<div id="div1"></div>
<div id="div2"></div>
<div id="div3"></div>
</div>
<div id="viewer"></div>
This snippet is made by Duannx.
Source: Please note that this is an extract of Vertical-Align: All You Need To Know written by Christopher Aue.
The question is why are vertical-align: baseline; ignored. Well I don't think it is. In your first snippet you're using baseline and bottom what clearly gives a difference between the two <span> elements. So what does baseline do? Well baseline Aligns the baseline of the element with the baseline of the parent element. In the example below I've copied and adjusted some parts to give the difference.
span.one {vertical-align:baseline}
span.two {vertical-align:middle;}
<p>
<span class="one">one</span> <span class="two">two</span>
</p>
As you can see the baseline alignment acts normal just as the middle alignment.
Now lets test something else. lets swap the alignment of baseline and middle and edit middle and add a third <span>
span.one { vertical-align: top;}
span.two { vertical-align: baseline;}
span.three {vertical-align: middle; height: 20px; }
p {
height: 50px;
background-color: grey;
}
<p>
<span class="one">one</span> <span class="two">two</span> <span class="two">two</span><br><br> <span class="three">three</span>
</p>
Now if you'll edit the second snippet and take a look at the vertical-align of <span class="three"> you can clearly see that when changing the alignment the text does change it's position.
But you're question relates on text on the same line so lets take a look at the snippet down below.
span.one { vertical-align: middle;}
span.two { vertical-align: baseline;}
span.three {vertical-align: middle; height: 20px; }
p {
height: 50px;
background-color: grey;
}
<p>
<span class="one">one</span> <span class="two">two</span> <span class="two">two</span><span class="three">three</span>
</p>
As you can see in this third snippet I've placed the third <span> next to one and two. And it does make a difference. The baseline in this case is different from the middle alignment. This is because the baseline grabs the baseline of the parent. since the parent's height is normal it doesn't affect baseline and top alignment. but when you'll use middle or sub it is clearly a difference.
For information about each alignment, check out this link.
A shorter explanation: A starting point is knowing where the baseline of the parent is. Add some text within the <p> tag or a <span> with vertical-alignment set to baseline (span "one" below). The baseline of span "one" is the bottom of the characters, e.g. the bottom of the letter 'n'.
Then its easy to see how the other spans change in relation to it. I added a border around span "one" so we can see its top and bottom edges clearly.
span { }
span:nth-child(1) {vertical-align:baseline; font-size:3em; border: 1px solid gray}
span:nth-child(2) {vertical-align:top; color:red}
span:nth-child(3) {vertical-align:middle; color:green;}
span:nth-child(4) {vertical-align:bottom; color:blue;}
<p>
<span>one</span> <span>two</span> <span>three</span> <span>four</span>
</p>

Centring a div box when you cannot adjust html

I am trying to adjust the CSS so the "product" and product information is centered and not floated to the left I cannot adjust the HTML as its given via a shortcode thats added into the WP post but maybe I could wrap it in a div?
HTML:
<ul class="products ribbon">
<li class="product last first">
<a href="http://dailybabydeals.co.nz/shop/estella-rose-designs/">
<div class="thumbnail">
<span class="onsale">Sale!</span>
<img width="325" height="325" src="http://dailybabydeals.co.nz/wp-content/uploads/2013/02/Front-Page-325x325.jpg" class="attachment-shop_catalog wp-post-image" alt="Front Page" />
<div class="thumb-shadow"></div>
<strong class="below-thumb">Estella Rose Designs</strong>
</div>
<span class="price"><span class="from">From: </span><del><span class="amount">$25</span></del> <ins><span class="amount">$19.95</span></ins></span>
</a>
<div class="buttons">
Select options</div>
</li></ul>
CSS:
CSS
Okay, let's try this again now that I understand your question better. So you want to center the <ul> element as a whole? That is a lot simpler than what I originally thought you were going for.
To do that, all you need to do is wrap the <ul> element in a span tag that is set to display as an inline-block. Then set the containing element so that its text is centered.
Try this:
<html>
<head>
<style language="text/css">
/*<![CDATA[ */
#test1 {
text-align: center;
}
#test2 {
display: inline-block;
text-align: left;
}
/* ]]> */
</style>
</head>
<body>
<div id="test1">
<span id="test2">
<!-- Place your <ul> element here -->
</span>
</div>
</body>
</html>
how it works
The "test2" element is set to display as an inline-block, which means it displays inline with the text. This means that it can then be affected by properties that manipulate lines of text, such as "text-align".
Setting "text-align" to "center" on the "test1" element makes the inline content -- the "test2" element in this case -- within it become centered on the screen.
The standard way to center a block is to set the "margin-right" and "margin-left" properties to "auto", but that only works for elements that are displayed as blocks and that have a width that is less than 100%.
I would just put it in a div and float it next to another div with nothing in it.
http://www.barelyfitz.com/screencast/html-training/css/positioning/
Like in step 8 in this link.
The reason that it looks like the text "Sale!" is floated to the left is that <img> elements display as inline blocks by default, so the image is on the same line of text as the words "Sale!". A <br /> tag immediately following the text "Sale!" would solve that problem; but you said you can't modify this HTML, right?
Given that restriction, here is how I was able to solve the problem...
Surround the HTML from your example in a <span> tag and assign it a class. I used "test" as my class name".
Then Place this CSS in the head of the HTML document:
<style language="text/css">
/*<![CDATA[ */
.thumbnail img {
display: block;
}
.test {
display: inline-block;
}
.test .price, .test .below-thumb {
display: block;
text-align: center;
}
/* ]]> */
</style>
why it works
The selector for making the image display as a block solves the problem of the missing <br /> tag.
The span tag with which you surrounded the HTML displays as an inline block. This means that it will collapse around its contents, giving you a box within which you can center the text.
Making the items that contain the text display as a block causes them to take a width of 100%, filling the surrounding box
The inclusion of "text-align: center" is what finally makes the text display as centered within its container

CSS: display:inline-block and positioning:absolute

Please consider the following code:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<style type="text/css">
.section {
display: block;
width: 200px;
border: 1px dashed blue;
margin-bottom: 10px;
}
.element-left,
.element-right-a,
.element-right-b {
display: inline-block;
background-color: #ccc;
vertical-align: top;
}
.element-right-a,
.element-right-b {
max-width: 100px;
}
.element-right-b {
position: absolute;
left: 100px;
}
</style>
<title>test</title>
</head>
<body>
<div class="section">
<span class="element-left">some text</span>
<span class="element-right-a">some text</span>
</div>
<div class="section">
<span class="element-left">some text</span>
<span class="element-right-a">some more text to force line wrapping</span>
</div>
<div class="section">
<span class="element-left">some text</span>
<span class="element-right-b">some text</span>
</div>
<div class="section">
<span class="element-left">some text</span>
<span class="element-right-b">some more text to force line wrapping</span>
</div>
<div class="section">
<span class="element-left">some text</span>
<span class="element-right-b">some more text to force line wrapping</span>
</div>
</body>
</html>
The element with absolute positioning apparantly makes the containing box "forget" which height he needs.
I need the absolute positioning inside the "section" box, is there another solution for this?
edit
Using tables is not really an option, I need some sort of "multi-level"/"nested" layout, where the second col is always on the same position:
| some text in first column | some text in 2nd column
| some indented text | 2nd column
| also indented | 2nd col
| even more indent | 2nd column with a lot of text that
| makes it wrap
| text | ...
| blah blah | ...
(of course whithout the "|"s)
When you use position:absolute;, the element is taken out of the normal page flow. Therefore it no longer affects the layout of its container element. So the container element does not take into account its height, so if there's nothing else to set the height, then the container will be zero height.
Additionally, setting display:inline-block; does not make any sense for an element that is position:absolute;. Again, this is because absolute positioning takes the element out of the page flow. This is at odds with inline-block, which only exists to affect how the element fits into the page flow. All elements that are position:absolute; are automatically treated as display:block, since that's the only logical display mode for absolute positioning.
If you need absolute positioning, then the only solution to your height problem is to set the height of the container box.
However, I suspect that you could do without the absolute positioning.
It looks like what you're trying to do is position the second <span> in each block to a fixed position in the block, so that they line up.
This is a classic CSS problem. In the "bad-old-days", web designers would have done it using a table, with fixed widths on the table cells. This obviously isn't the answer for today's web designers, but it is something that causes a lot of questions.
There are a number of ways to do it using CSS.
The easiest is to set both the <span> elements to display:inline-block;, and give them both a fixed width.
eg:
<div class='section'>
<span class="element-left">some text</span>
<span class="element-right">some text</span>
</div>
with the following CSS:
.section span {display:inline-block;}
.element-left {width:200px;}
.element-right {width:150px;}
[EDIT]after question has been updated
Here's how I would achieve what you're asking now:
<div class='section'>
<span class="element-left"><span class='indent-0'>some text</span></span>
<span class="element-right">some text</span>
</div>
<div class='section'>
<span class="element-left"><span class='indent-1'>some text</span></span>
<span class="element-right">some text</span>
</div>
<div class='section'>
<span class="element-left"><span class='indent-2'>some text</span></span>
<span class="element-right">some text</span>
</div>
with the following CSS:
.section span {display:inline-block;}
.element-left {width:200px;}
.indent-1 {padding:10px;}
.indent-2 {padding:20px;}
.element-right {width:150px;}
A small amount of extra markup, but it does achieve the effect you want.
No.
You could position absolutely then have a copy of the element inside the section box with visible: none but absolute positioning by definition makes it a "floating" element that doesn't interact with elements above it.
Assuming your page layout is fixed you could use padding-left: #px; to achieve your goal, as I don't think relative positioning is what you want.
Alternatively, you could use display: table-* to force it to retain a stricter form without affecting the document structure as shown here
However depending on whether you want the cells to be fluid you may need to alter the .section divs into display: table-row if you don't like a predetermined width setup and want the separate .section's to line up.
This can in fact be done easily and simply with divs. You just need to place a div with position:relative inside the inline or block display div. Set its width and height to the same as the containing div. You will then find you can position another div absolutely inside it.

Two divs; left should be fixed width, right should fill rest of space

I've got the following HTML code:
<body>
<div id="Frame">
<div id="Body">
<div id="Panel">Side panel, fixed width.</div>
<div id="Content">The rest of the content, should be dynamic width and fill up rest of space horizontally.</div>
</div>
<div id="Foot">
<div>FooBar.</div>
</div>
</div>
</body>
What I'm trying to do is make it so that #Panel is of a fixed width (~200 pixels) and on the left hand side, and that #Content is immediately to the right of #Panel but is of "dynamic" width and fills the rest of the space in the browser screen horizontally. I've tried a lot of different things but haven't been able to get it working -- the farthest I've gotten is to the point where #Panel is on the left and #Content is to the right of #Panel and fills of the rest of the space, but #Content starts below #Panel whereas I'd like it to start at the same vertical position.
I did find In CSS, how do I get a left-side fixed-width column with a right-side table that uses the rest of the width?, however I wasn't able to apply it to the HTML above.
Here's that link, applied to your code:
CSS
#frame { background:pink }
#panel { background:orange; width:200px; float:left }
#content { background:khaki; margin-left:200px }
#foot { background:cornflowerblue }
HTML
<div id='frame'>
<div id='body'>
<div id='panel'>
Side panel, fixed width.
</div>
<div id='content'>
The rest of the content, should be dynamic width and fill up rest of space
horizontally.
</div>
</div><!-- End #body -->
<div id='foot'>
<div>FooBar.</div>
</div>
</div><!-- End #frame -->
Works pretty well! Although, IMHO, you don't need the frame or body (but I don't know the master plan). That would look like this:
<div id='panel'>
Side panel, fixed width.
</div>
<div id='content'>
The rest of the content, should be dynamic width and fill up rest of space
horizontally.
</div>
<div id='foot'>
<div>FooBar.</div>
</div>

Resources