Whitespace at end of span affects absolute positioning of after pseudo element - html

In styling a component on my site, I found that adding whitespace to the end of my span that's inside an h2 affects the absolute positioning of span:after in a weird way. The after element I use as a border. The border positions itself after the content without the whitespace, but positions itself over the content when there is whitespace. You can see an example here.
Any ideas on why this would render differently?
html:
<h2>
<span>A test</span> <!-- the border appears 'after' the content -->
</h2>
<h2>
<span>A test </span> <!-- the border appears 'over' the content -->
</h2>
css:
h2 {
position:relative;
height:1.625em/1;
}
h2 span {
font-size: 100%;
margin: 0;
padding: 0;
vertical-align: top;
display: inline-block;
1.625em/1 'MuseoSlab300','Times New Roman',serif;
}
span:after {
content: "";
border-bottom: 5px solid #7c61a0;
position: absolute;
bottom: 5px;
margin-left: 8px;
width: 100%;
}

I think the major difference between those two cases (with the trailing space vs. without) is that a browser will typically wrap content, if needed, at whitespace. So in the first case (with trailing space), the browser has no choice but to push out the width of that span, because the :after pseudo-element can't wrap. In the second case, the browser uses the position:absolute declaration on the pseudo-element as a key to preserve the width of the original span, and with the whitespace there before the pseudo-element, the browser can then wrap the pseudo-element onto the next line. You'd see this effect even if the width of the pseudo-element were set to something small like 10px instead of 100%.
Since this appears differently in Firefox vs. Chrome, I'm not sure which one is actually behaving according to standards, or if this case is even specified.

Related

CSS vertical align trick of parent not working with percentage img image inside child div

As I found out, the best browser friendly solution for vertical aligning is the trick with a pseudo element. But it's not working if I need to use percentages with image inside another div.
Here is a fiddle of my problem.
I need to work with percentages due to responsive design.
I realize that the problem is may be caused by the "width" of the pseudo element, because when I change the width of the child element to 99%, it jumps back where it should be, but why is this necessary? I don't want to use 99% as this can cause problems (when shrinking browser window it has to be eventually changed to 98%,97%...) and image is not touching sides of it's parent element. Does anybody know the reason? Thanks.
html, body {
height: 100%;
width: 100%;
margin: 0px;
padding: 0px;
}
.parent {
width: 100%;
height: 100%;
}
.parent:before {
content: "";
display: inline-block;
height: 100%;
vertical-align: middle;
}
.child {
display: inline-block;
height: 25%;
width: 100%;
}
img {
width: 100%;
height: 100%;
}
<div class="parent">
<div class="child">
<img src="http://www.resortcollection.com/wp-content/themes/resortcollection/property-images/summit/summit-beach-resort-panama-city-beach-fl-beach-01.jpg">
</div>
</div>
The trick is to remove all whitespaces between parent and child divs, so there is no whitespace between :before and child. And also you should remember to add vertical-align to child
Example fiddle
Yes. When you put the :before pseudo-element in place it goes right at the start of the content, that is, before any spaces. So your horizontal layout is
before-pseudo single-collapsed-Space img
Zero width 4px (from the font size) 100% of container
Which is too big. Use margin-left:-4px; on the pseudo element to compensate.
See https://jsfiddle.net/xoks5f1e/5/
I will suggest my own solution that I've mysteriously did not see earlier. It is a solution that most of professional webdesigners use. And that is to remove the space/line-break between tags styled with display: inline-block.
Important note: When applying ::before pseudo element on some parent element (e.g. .parent), CSS will append this new pseudo element directly "before" the first child element of the node .parent. That means if the first child element of .parent is separated from the opening .parent tag by a new paragraph character or a space, then this white space character will also be before the newly appended pseudo element. And because of that, when using display: inline-block the white-space character will also affect CSS's pseudo element.
The solution in case you have the access to the HTML file, is:
First child of .parent must be directly after .parent's opening tag as #DenisSheremet suggested.
Make a real element instead of "pseudo" one in real DOM:
<div style="display: inline-block; height: 100%; vertical-align: middle"></div><
div class="child">
or more elegantly using comments:
<div style="display: inline-block; height: 100%; vertical-align: middle"></div><!--
--><div class="child">
If you have only access to CSS, than the only other choice what I know so far is the solution suggested by #Alohci to remove the white space with default font space width of 4px, using negative margin.
JSFiddle

floating content in div and hr

The content of hr tag flow around floating elements as if it is inline elements (even if it is actually blocks). That's what I need but unfortunately hr can't have child elements except two pseudo elements.
Take a look on this demo on JsFiddle: http://jsfiddle.net/P3KEZ/
<div id="right"></div>
<div class="divider"></div>
<hr class="divider" />
#right{
background: #ffaaaa;
width: 200px;
height: 300px;
float: right
}
.divider {
background: #4d9d4d;
height: 20px;
border: none;
position: relative;
}
.divider:after, .divider:before {
content: " ";
width: 20%;
height: 100%;
display: inline-block;
position: absolute;
background: #a2a2f2;
top: 0;
}
divider:before {
left: 0;
}
.divider:after {
right: 0;
}
What I actually want is to get element with content flow around the floating elements (like hr do) but also can have at least 3 child elements (like div can do).
So question is: how to emulate such behaviour in div? (without display: flex)
What I actually want is to get element with content flow around the floating elements (like hr do) but also can have at least 3 child elements (like div can do).
So question is: how to emulate such behaviour in div?
You want to harvest the power of the mighty overflow property … (*thunderclap*)
.divider {
/* … */
overflow:hidden;
}
Normally, a block element is layed out behind a floating element, only its inline content floats next to the floated element – but with overflow:hidden you can change that, so that a block element like div only takes the space that is left beside the floating element. (It does not actually have to be hidden – everything besides the default value visible will trigger this behavior, so you can use auto or scroll as well if those suit your actual use-case better.)
See here: http://jsfiddle.net/P3KEZ/1/

Collapsing margin alignment in Firefox

TLDR: this codepen works fine in Chrome, but the alignment is off in Firefox.
I'm building a jQuery plugin which modifies a text input to give it a dropdown button on the left. In order to get the positioning right, I add a wrapper div, which is the same height as the input, so the button can be absolutely positioned on top of the input, and yet still have the same height:
#wrapper {
position: relative;
}
#overlay {
position: absolute;
top: 0;
bottom: 0;
width: 30px;
}
This works fine until the input has vertical margin: then the container grows to include the margin, and so the dropdown button grows with it. My solution to this was margin collapsing: I gave the input display:block which meant that the container ignored it's margin. All good.
input {
margin: 20px 0 40px; /* testing */
display: block;
}
But now the problem is that by default, inputs are inline elements e.g. you might want to have a submit button next to the input. So I wrapped the whole thing in a container div with display:inline-block, so another inline element like a button can happily sit next to it.
#container {
display: inline-block;
}
This works fine in Chrome, but has weird alignment issues in Firefox when there's any vertical margin on the input. Below I've added the final markup. There's also a codepen link at the top.
<div id="container">
<div id="wrapper">
<input>
<div id="overlay"></div>
</div>
</div>
<button>Submit</button>
Edit: the point is that this is a plugin and I'm trying to work with the user's existing markup and CSS e.g. they have this markup:
<input><button>Submit</button>
and their existing CSS has vertical margin on the input, and I want them to be able to just initialise my plugin on the input and it just work, without forcing them to change their markup/CSS. Now because the plugin needs to add lots of markup around the input (for the overlay and the dropdown list), I wrap it all up in a container div. This container div is the limit of our reach (and does not include the button element, or anything else they choose to put next to their inputs).
To fix this, you'll need to define a line-height in your parent div#test2. Without it, different browsers will give it different values. This will cause Firefox to cause this weird result.
Now, the line-height isn't the only problem, also the vertical-align's baseline value will generate a different result for inline elements than it is for inline-block elements that have a different height than the surrounding inline content. To fix this, change the value to top for the #container element (since that's the inline-block element).
The final result would have the following changed (only pasting the parts that changed):
#test2 {
background-color: green;
line-height:70px;
#container {
// replicate the inline nature of the input
display: inline-block;
vertical-align:top;
}
//the rest of the #test2 nested code
}
That would look like this.
Reply to comment
I've made something that does work by the requirements set. Since you said the extra code (so the divs around the input) are made by the plugin itself, I've taken the liberty of changing that a bit to make this work.
The way it can work quite easily is just not using inline-blocks at all, and sticking with the inline elements. This would change the styles to the following:
#container {
// replicate the inline nature of the input
display: inline;
}
#wrapper {
display: inline;
position: relative;
}
input {
// you'll want to make sure the typed text doesn't appear behind the overlay
padding-left:35px;
}
#overlay {
position: absolute;
top: 0px;
bottom: 0px;
left: 1px;
width: 30px;
background-color: #00C2FF;
}
Notes:
I didn't bother making the overlay cover the full height of the input, since your plugin would just make it a flag anyway. To make it cover the full height, just set negative top and bottom styles on the overlay, equal to the computed padding-top and padding-bottom (resp.) on the input. In this case, you'd have to change them to top:-5px;bottom:-5px;. (you can get the computed style via jQuery's $(input).css('padding-top'))
You could actually also remove the whole #container from it, since the only style it has now is display:inline which really doesn't add anything to the whole thing.
I've added a padding-left to your input, because otherwise you'd have to type behind the overlay, which is just silly.
Is the HTML generated by the plugin and it needs to stay exactly the same? I'm not sure I can figure out exactly why the second example is not working, but you seem to have too many div elements there. You could make since simpler:
HTML
<div id="test1">
<div id="wrapper">
<input>
<div id="overlay"></div>
<button>submit</button>
</div>
</div>
SCSS
input, button {
border: 1px solid black;
padding: 5px;
}
input {
display: inline-block;
padding-left: 35px;
}
#test1 {
background-color: yellow;
padding: 20px 0 40px 0;
#wrapper {
position: relative;
#overlay {
position: absolute;
top: 1px;
left: 1px;
width: 30px;
background-color: #00C2FF;
}
}
}
Codepen example
I've removed the margin, and instead used padding on the parent, it achieves the same thing. You'll also want some padding-left on your input field so the entered text doesn't disappear behind your overlay div.
EDIT: In case you are unable to change the markup:
SCSS:
#test2 {
background-color: green;
#container {
// replicate the inline nature of the input
display: inline-block;
padding: 20px 0 40px 0;
}
#wrapper {
// this is just here to be display:block and ignore the margin on the input
display: block;
position: relative;
}
input {
// tell parent to ignore margin
//display: block;
margin: 0;
}
#overlay {
position: absolute;
top: 1px;
bottom: 1px;
left: 1px;
width: 30px;
background-color: #00C2FF;
}
}
codepen demo
Removed the block and margin declarations from the input field, and moved the spacing to padding of the #container element.
"Disclaimer": Let me just start by saying that I did not find exactly what is causing the problems in Firefox, but I did think of an alternative way you could do it.
The way this works in both Firefox and Chrome is just to use the exact same HTML as you used for your #test1, but on top of that, also using the CSS :before pseudo-element (instead of using the #container and #wrapper). The code I used was:
#test2 {
background-color: green;
position:relative;
&:before {
content:"";
display:block;
position:absolute;
left:1px;
top:1px;
bottom:1px;
margin:20px 0 40px 0;
width:30px;
background:#00C2FF;
}
}
demo
The way this works is to simply position the :before overlay on exactly the same place as the divs previously were. As you can see, I've used most of the same styles as you did, but instead, I've put them on the :before pseudo-class.
Other answers don't know why it doesn't work on Firefox. Well, I think that Firefox has the right behavior and it's a Chrome problem.
In short, you want to align an input with a button. But the input is inside a wrapper. Then, you can use vertical-align to control the vertical aligning between the wrapper and the button, but not between the input and the button.
Here you can see an screenshot with different vertical-align:
See the code.
If you want to align the input and the button (last case in the image), you have a problem, because any of the keywords you can use with vertical-align does that. Only in case that input's top margin and bottom margin are equal, then vertical-align: middle works.
But in general, you have have another solution: vertical-align also accepts a <length> value.
And, to get a perfect alignment, you should use the formula
vertical-align: -(input bottom margin)
Or, if you want to align them even if the button has a bottom margin, then
vertical-align: -(input bottom margin) + (button button margin)
The code formula above works with inline-block <div>, but not with <buttons>.
The formula must be fixed to
vertical-align: -(input bottom margin) -(input offsetHeight)/2 + 6
In your example
(Input bottom margin) = 40px
(Input offsetHeight) = 31px
Then, you need
vertical-align: -(input bottom margin) -(input offsetHeight)/2 + 6
Demo
I could achieve it with the following.Codepen You will have to know the css applied to input and apply it to button as well
button{
position:absolute;
margin-left:5px;
}
input, button {
display: inline-block;
margin: 20px 0 40px 0;
}
please update below in your code.
input, button {
border: 1px solid #000000;
margin: 20px 0 40px;
padding: 5px;
vertical-align: top;
}
hope it will work
http://codepen.io/anon/pen/Isycu

How can I lay out two <div>s on one line, and have one centre-aligned (relative to the entire line) and the other right-aligned?

I want to display 2 divs in a single line. I have a parent div and two child divs.I want to keep the width of first child div and parent div equal. So the header(label of first child div) displays always middle position of parent div and I want to display the second child div at the right side in the same line of parent div.(Condition is always label of first child div should display middle of parent div). Here is the jsfiddle.
If I were styling this header section for a website, and I wanted some flexibility in styling the various elements, here is out I would start.
For my HTML:
<div class="head">
<div class="innerfirst">
<h1>ABCDEF GHIJ</h1>
</div>
<div class="innersecond">
<label>RIGHT1</label>
<label>RIGHT2</label>
</div>
</div>
I would put the page title in a <h1> tag so that I can adjust font-size, padding, background color and so on. In fact, you could add a tag line below the title line and various background images. Having .innerfirst and h1 gives you quite a bit of flexibility.
The <label> tags don't make sense semantically in this context, but perhaps you will have have input fields later like a search box.
For the CSS:
.head {
background-color:#2191C0;
width: 100%;
height: 85px;
position: relative;
}
The above is fine, set position: relative so that you can use absolute positioning for one of the child elements. The fixed height is a good idea, makes it easier to adjust elements vertically.
.innerfirst {
}
.innerfirst h1 {
text-align: center;
color: #FCFCFC;
padding-top: 10px; /* You could also use a margin... */
}
By default, .innerfirst will have 100% width since it is an in-flow block element, same with the h1 element. You can center the text within h1, and adjust color, padding and margin as needed.
.innersecond {
border: 2px solid lightgray;
color: white;
position: absolute;
width: 25%; /* Set this or by default it will shrink-to-fit content */
height: 61px; /* Set this or by default it will shrink-to-fit content */
top: 5px;
right: 5px;
padding: 5px;
}
What you could do is create a box of text and absolutely position it to the right. It is a good idea
to set a height and width otherwise, as a result of the absolute positioning, the div will shrink to fit the content, which is sometimes useful. The top and right offsets will position the .innersecond to the top-right of the parent container because you set position: relative in .head.
.innersecond label {
display: block; /* optional if you want block behavior */
border: 1px dotted white;
}
Finally, if you want the label tags to behave like blocks, use display: block and style according to you design requirements.
For reference, demo fiddle: http://jsfiddle.net/audetwebdesign/qpb9P/
Here's an updated jsfiddle. Read up on the display property!

percentage width with percentage padding

I have the layout here: http://jsfiddle.net/chricholson/susXf/11/
In Chrome and Firefox you'll notice there is a 1 pixel gap between the edge of the red box and the image. In IE 8, this isn't there (as expected).
This does not happen if I specify the width and padding using pixels.
My guess, although I cannot be sure, is due to the calculations of percentages and how numbers are rounded. The 6% for the padding works out to be 20.94px, the width of the p works out as 328.06. Assuming both are rounded down (despite the fact the first should be rounded up), then the total width is 348px, which seems to be the cause of the problem. IE is maybe more intelligent and rounds up correctly?
Nevertheless, has anyone else come across the same situation and found a fix?
The error is actually caused because of your parent element. You have the width set at 349px. Some browsers, depending on available screen space, will round up or down by default.
It's generally a good practice to use nice widths when using percentages.
Solution: http://jsfiddle.net/vonkly/susXf/29/
This solution allows for an unset, arbitrary image width to define the wrapping element's width and thus, the child span and <p> widths. This achieves the goal of the OP on the fly, without having to manually enter custom classes or width declarations.
CSS
img {
width: 100%;
max-width: 100%;
}
.videoThumb {
position: relative;
display: inline-block;
padding: 0;
}
.videoThumb .details {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
color: #000;
background-color: red;
}
.details p {
display: block;
padding: 14px 4%;
margin: 0;
}
HTML
<div class="videoThumb">
<img src="http://www.chainreactiondisco.com/images/disco_panel_02_01.jpg">
<span class="details">
<p><!-- containing element for black box -->
Watch<br />
Signature Manager vs. Mail Dislcaimers
</p>
<span><!-- /details -->
</div><!-- /videoThumb -->
NOTE
If you are planning on using any <p> tags inside span.details, I'd suggest changing your css a bit. All you need to do is change .details p {} to .details span {}. Then go to your HTML and change the <p> tags to <span> tags.