Firefox max-width not working in nested div - css

So I have an issue that appears on Firefox.
Basically I have a container with set width (let's say 300px).
Inside it nested a couple of levels is a component that uses truncation to hide text with max-width. CSS rule would look like this for example:
max-width: calc( ((100vw - 376px) / 12) * 8 + 168px );
Child span uses basic line-clamp + overflow technique for hiding extra content.
What I found out is that this code constrains width of elements of component and truncation works fine on Chrome but it doesn't work as expected in Firefox. I guess that it makes sense since this calculation uses vw for setting max-width but it confuses me why it works on Chrome. Does Firefox somehow differently do calculations?
.wrapper {
width: 300px;
}
.max-width__component {
/* This is probably calculated to be a bigger value than width
of container */
max-width: calc( ((100vw - 376px) / 12) * 8 + 168px);
}
.truncation {
display: -webkit-box;
text-overflow: ellipsis;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
<div class="wrapper">
<div class="max-width__component">
<span class="truncation">
Some dummy content that is normally longer and truncated
</span>
</div>
</div>
Thanks in advance.

The problem indeed wasn't with setting max-width
One thing that was missing from my code snippet was another child element that represents icons (https://codepen.io/Skafec/pen/yLbNBJM) but the issue still wasn't reproduced in this codepen.
How I fixed is that I added:
flex-shrink: 0;
on icon wrapper which makes sure that icon always takes the same space and doesn't get pushed outside of the main container.

Related

100% height body is taller than 100vh, caused by Angular's router-outlet

I have an angular page, home, which is comprised of 2 components and a router-outlet
<div class="home-container">
<header></header>
<sub-header></sub-header>
<router-outlet></router-outlet>
</div>
I want the home-container above to always be, at a minimum, full screen height. The header should show, then the sub-header, then the contents of the router-outlet should always fill up at least the rest of the screen (or more if there's more content of course).
Normally this is easy but it seems the router-outlet is messing it up. Example can be seen http://plnkr.co/edit/56k9ZabLAGujBoX8Lsas , hit run and then click the "Heroes" link to route. In this example I don't want the Heroes div to be taller than the screen, and don't understand why it is.
My styles to accomplish this are. (assume router-outlet is on 'my-page')
html, body {
height: 100%;
}
.home-container {
height: 100%;
}
.my-page {
height: 100%;
}
My expectation here obviously is that home-container is full screen, shows header, shows sub-header, and that my-page then fills in at a minimum the rest of the vertical height.
What is actually happening though, is that there's a scroll bar with available height that appears equal to my header and sub-header.
This plnkr http://plnkr.co/edit/56k9ZabLAGujBoX8Lsas illustrates exactly my meaning. If you click Run and then the link for "Heroes" you will see the router-outlet contents, in this case heroes-list.component, with a green background. I do not understand why the green here is bleeding below the screen when everything is set to 100%
Update I have tried using all manner of different CSS attributes to different levels in this nesting. Including 100vh vs 100%, min-height vs height, and every combination of body/html/home-container/my-page. I have also tried the same with Angular's CSS :host, to the same result of no different
Update2 If I move it out of the element then everything behaves as you'd expect and there's no vertical scroll bar. Something about the router-outlet wrapper adds vertical space somewhere but I cannot figure out where or what is causing it.
Final Update The below answers might be useful for some applications but I ended up just solving it by giving the .my-page a specified height, just doing height: calc(100vh - $headerheight - $subheaderheight) which gets the job done
As far as I understand, 100% on a child will be equal to the size of the parents natural height. If you want to fill the space available, you really should be using flex unless you have a requirement to support IE9 and below.
I would update your Anchors to be contained in a div (or another wrapper)
<h1 class="title">Component Router</h1>
<div>
<a [routerLink]="['CrisisCenter']">Crisis Center</a>
<a [routerLink]="['Heroes']">Heroes</a>
</div>
<router-outlet></router-outlet>
I would then utilize flexbox to allow the content to expand as required
.hero-list {
background-color: green;
height: 100%;
overflow:auto
}
undefined {
flex: 1;
}
body, html, my-app {
height: 100%;
}
my-app{
display: flex;
flex-flow: column;
}
Plunker to test: http://plnkr.co/edit/yE1KOZMr1pd5jQKlVYIN?p=preview
On chrome i still have scroll bars due to an 8px margin on body - this can easily be removed with CSS for a scroll free full height experience.
There are two causes that make your <body> element taller than 100% of the viewport:
Default margins of the <body> element that come from the browser's built-in styles and usually are 8px. This means that the <body> element will be as tall as the <html> element, but also will have 8px space above it and below it, causing the <html> element to overflow.
The top margin of the <h1> element "falls out" from the container due to margin collapsing. This makes the space above the <body> element equal to the default top margin of <h1> (about 21px instead of 8px).
Setting zero margin to <body> (part of ToTaTaRi's answer) helps you to solve the 1st issue. To solve the second one, you should make the <body> element or (probably better) the .my-app container establish the new Block Formatting Context. The easiest and most cross-browser way for this is setting the container overflow:hidden (other options are display:flow-root, which works for modern Chrome/Firefox, or column-count:1, which works in IE10+ and all modern browsers, you can compare nearly all the options in this live example).
First of all you should reset browser default styles at least somehow like this:
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
Then you could achive what you want without a flex layout if prefered through splitting the page into a header section and main content section with a preset division... So lets say the heading and the links go together into a container div with i.e. a height of 20% and the main content which is at the moment hold in a tag "undefined" gets a height of 80%, if you now set the height of the app container to 100% or 100vh it should work as expected!
EDIT (because the topic is still open...):
Have you tried this css code like explained above, works like charm!?
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body, my-app {
height: 100%;
height: 100vh;
}
h1 , h1 + div {
height: 10%;
height: 10vh;
}
undefined {
display: block;
background-color: green;
min-height: 80%;
min-height: 80vh;
}

The height of the code-mirror block are not the same in Chrome and Safari

I want to make a layout that satisfies the following conditions:
1) it has a block on the top whose height is up to its content
2) below it has a code-mirror and a block side by side, which fill in exactly the rest of the page in terms of height.
I have made a plunker here. The problem is it works well in Chrome 57.0.2987.133, whereas it does NOT work well in Safari 10.1: the height of the code-mirror is NOT enough; it shows only 76 lines of the code rather than the correct 80 lines.
Does anyone know how to fix this?
<style>
.rb {
display: flex;
flex-direction: column;
height: 100%;
}
.rb .container {
flex: 1;
display: flex;
width: 100%;
height: 100% /* new */
}
.rb .first-row {
border: 1px solid black;
/*flex: 0 0 60px;*/
}
.rb .CodeMirror {
flex: 1;
height: auto;
}
.rb .flex-preview {
flex: 1;
border: 1px solid black;
}
</style>
<div class="rb">
<div class="first-row">
1<br/>2<br/>3<br/>4<br/>
</div>
<div class="container">
<textarea ng-model="body" ui-codemirror="option"></textarea>
<div class="flex-preview">
</div>
</div>
</div>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<ui-view></ui-view>
</div>
<script>
var app = angular.module("myApp", ['ui.router', 'ui.codemirror']);
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('global', {
templateUrl: 'template.html'
})
}]);
app.controller('myCtrl', ['$scope', '$state', function ($scope, $state) {
$scope.option = { mode: 'text/html', lineNumbers: true, matchBrackets: true };
$scope.body = ""
for (var i = 1; i <= 79; i++)
$scope.body = $scope.body + "a\n";
$state.go('global')
}])
</script>
</body>
.rb .container {
flex: 1;
display: flex;
width: 100%;
height: auto /* new */
}
Try using height as 'auto'.
According to http://www.caniuse.com, there are few known issues with Safari re vh:
Safari & iOS Safari (both 6 and 7) does not support viewport units
for border widths, column gaps, transform values, box shadows or in calc().
iOS 7 Safari sets viewport unit values to 0 if the page has been left
and is returned to after 60 seconds.
iOS 7 Safari recalculates widths set in vh as vw, and heights set in vw
as vh, when orientation changes.
vh on iOS is reported to include the height of the bottom toolbar in the
height calculation, and the width of the sidebar (bookmarks) in the vw
width calculation.
As you have set border-widths in the code posted, the use of vertical height (vh) as a measurement unit is going to pose a problem for you.
There are a few workarounds I suppose, you could use percentage, adjust the vh if safari using browser detection (modernizer?) or you could perhaps add a margin or padding? Just some thoughts off the top of my head. Good luck.
Hope this helps
EDIT: Your issue may lie with the use of flex which unfortunately has issues in Safari which is marked as fixed because it used to appear in Chrome also, but was fixed in Chrome 51. It still occurs in Safari according to caniuse
In Safari, the height of (non flex) children are not recognized in percentages.
However other browsers recognize and scale the children based on percentage heights. (See bug)
The bug also appeared in Chrome but was fixed in Chrome 51
The indication that non-flex children is NOT measured in % would suggest that vh may be used instead (which leads back to my earlier answer).
On the upside, it's not just you that's experiencing the issue! It's a known bug that should be fixed in a future release. Every cloud has a silver lining.. :)
EDIT #2
Another issue with Safari (iOS) is that it doesn't support min-width (actually only on table elements on second look) in CSS. Your externally linked in stylesheets may be using min-width so this may also have an effect on the output. It is recorded as happening in iOS 5.1 but it is unclear whether it was fixed in later versions.
You're setting the height of the code-mirror container (.rb) using height: 100%. But you don't have any height specified on the parent container.
When you use percentage heights, some browsers still require you to define a height on the parent (using the height property). That's been the traditional implementation of the spec language, although not all browsers adhere to that interpretation anymore.
However, if you're going to use height: 100% on an element, and you want to ensure cross-browser support, make sure the parent container has a defined height. If the parent also uses a percentage height, then its parent must also have a defined height.
Alternatively, just use height: 100vh on .rb and be done with it.
Then your flex: 1 on .container will work to consume available height.
More information and other solutions here:
Chrome / Safari not filling 100% height of flex parent
Working with the CSS height property and percentage values

Is it possible to calculate the Viewport Width (vw) without scrollbar? [duplicate]

This question already has answers here:
100vw causing horizontal overflow, but only if more than one?
(8 answers)
Closed 4 months ago.
As mentioned in the title, is it possible to calculate the vw without the scrollbars in css only?
For example, my screen has a width of 1920px. vw returns 1920px, great. But my actual body width is only something like 1903px.
Is there a way for me to retrieve the 1903px value with css only (not only for direct children of the body), or do I absolutely need JavaScript for this?
One way to do this is with calc. As far as i know, 100% is the width including scrollbars. So if you do:
body {
width: calc(100vw - (100vw - 100%));
}
You get the 100vw minus the width of the scrollbar.
You can do this with height as well, if you want a square that's 50% of the viewport for example (minus 50% of the scollbar width)
.box {
width: calc(50vw - ((100vw - 100%)/2))
height: 0
padding-bottom: calc(50vw - ((100vw - 100%)/2))
}
I do this by adding a line of javascript to define a CSS variable once the document has loaded:
document.documentElement.style.setProperty('--scrollbar-width', (window.innerWidth - document.documentElement.clientWidth) + "px");
then in the CSS you can use var(--scrollbar-width) to make any adjustments you need for different browsers with/without scrollbars of different widths. You can do something similar for the horizontal scrollbar, if needed, replacing the innerWidth with innerHeight and clientWidth with clientHeight.
COPY & PASTE solution
Here is an easy drop-in solution based on user11990065's answer to set a css variable --scrollbar-width and keep it updated on resizes.
It also gets calculated on DOMContentLoaded and load events so that you don't have to worry about size changes during the initial rendering phase.
You can just copy and paste it to your code as it is vanilla JS (or wrap it in a 'script' tag and paste it directly into your HTML code:
function _calculateScrollbarWidth() {
document.documentElement.style.setProperty('--scrollbar-width', (window.innerWidth - document.documentElement.clientWidth) + "px");
}
// recalculate on resize
window.addEventListener('resize', _calculateScrollbarWidth, false);
// recalculate on dom load
document.addEventListener('DOMContentLoaded', _calculateScrollbarWidth, false);
// recalculate on load (assets loaded as well)
window.addEventListener('load', _calculateScrollbarWidth);
If you have dynamic height changes in your page that might show / hide the scrollbar, you might want to look into Detect Document Height Change with which you can trigger the recalculation also on height changes.
As the value is calculated with JS and set to a fixed value you can use it in calc operations in your CSS, like so:
.full-width {
width: calc(100vw - var(--scrollbar-width));
}
This will give .full-width exactly the available width.
According to the specs, the viewport relative length units do not take scrollbars into account (and in fact, assume that they don't exist).
So whatever your intended behavior is, you cannot take scrollbars into account when using these units.
body { overflow: overlay; }
If you don't want to overcomplicate things, this might be sufficient in certain situations. At least it fixed my issues well enough, since there was enough whitespace between the content and the viewport edges (Windows scrollbar would overlap your 20-ish most right pixels).
Webkit browsers exclude the scrollbars, other include them in the returned width.
This may of course lead to problems: for instance if you have dynamically generated content with ajax that add height dynamically, Safari might switch from a layout to another during page visualization...
Ok, it doesn't happen often, but it's something to be aware about.
On mobile, less problems, cause scrollbars are generally not showed.
That's said, if your problem is calculate exactly the viewport width without scrollbars in all browser, as far as i know, a good method is this:
width = $('body').innerWidth();
having previously set:
body {
margin:0;
}
100vw = width of the screen with scrollbar
100% = width of the screen without scrollbar
It is always preferable to use calc(100% - 50px) while measuring the screen width. Even on windows browsers where scrollbar is visible directly, return the screen width differently when compare with macOS browsers.
It's possible just very "ugly" looking.
First you need to have this script running to get the scrollbar width into a css variable:
document.documentElement.style.setProperty('--scrollbar-width', (window.innerWidth - document.documentElement.clientWidth) + "px");
Now for example if you want "real" 80vw do this:
calc(0.8 * (100vw - var(--scrollbar-width)));
"real" 40vw
calc(0.4 * (100vw - var(--scrollbar-width)));
As long as you're not expecting any actual horizontal scroll, you could use this:
body {
overflow-x: hidden;
}
Which will then just hide the tiny amount of horizontal scroll caused by the auto scrolling Y.
I came across this question while looking for an answer for my case.
I wanted to use WordPress's solution to center a div on the viewport with the viewport's width just like .alignfull would normally.
Situation:
<html>
<body>
<div class="main">
<div class="continer">
<div class="row">
<div class="col-12">
<article>
<div class="content">
<div class="alignfull-or-alignwide">
<p>The content.</p>
</div>
</div>
</article>
</div>
</div>
</div>
</div>
</body>
</html>
My solution:
html {
width: 100vw;
overflow-x: hidden;
}
.alignfull-or-wide {
margin-right: calc(50% - 50vw);
margin-left: calc(50% - 50vw);
width: 100vw;
max-width: 100vw; // change this for wide or w/e.
}
This solved my problem by making the root of the document as wide as the viewport. With this, you essentially ignore the width of any scrollbar.
By setting to 100vw we eliminate the width of the scrollbar on any platform.
By setting the overflow parameter, we prevent any content from being rendered outside of the viewport.
By setting margins, we center the left side of the div to it's relative positioned parent. This usually is the center of the viewport too.
Then, the negative margin pulls it to the left side of the viewport.
By doing the same on the right we create the illusion of the div being centered on the page.
Also something to watch out for: scrollbar-width on csswg.
The only way I found it to work without messing your code with "calc"
is to make the container element size to 100vw; Adding a wrapper around the container for overflow-x; This will make the container to be fullwidth like if the scrollbar was over the content.
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
html{ overflow-y: scroll; }
html, body{ padding:0; margin: 0;}
#wrapper{ overflow-x: hidden; }
.row{ width: 100vw; }
.row:after{ clear: both; content: ''; display: block; overflow: hidden; }
.row-left{ background: blue; float: left; height: 40vh; width: 50vw; }
.row-right{ background: red; float: right; height: 40vh; width: 50vw; }
</style>
</head>
<body>
<div id="wrapper">
<div class="row">
<div class="row-left"></div>
<div class="row-right"></div>
</div>
</div>
</body>
</html>
The vw unit doesn't take the overflow-y scrollbar into account when overflow-y is set to auto.
Change it to overflow-y: scroll; and the vw unit will be the viewport with the scrollbar. Then you can subtract the scrollbar size from the vw value using calc(). You can also define the scrollbar width, so it will be browser-independent.
Only downside to take into account. If the content fits into the screen, the scrollbar is shown anyway. Possible solution is to change from auto to scroll in javascript.
No, there's no way to calculate the vw without the scrollbars in CSS.
However, there's a way to solve the 100vw ruined by the scrollbar on Windows issue. You have to create a full-width element, in this case row--full-width, that beelds out of a Flex container. This solution works on both Mac and Windows:
HTML:
<section>
<div class="container">
<div class="row--full-width"></div>
<div class="row">
<div class="card">
</div>
<div class="card">
</div>
</div>
</div>
</section>
Example: https://jsfiddle.net/ecmv6ho1/show
Code: https://jsfiddle.net/ecmv6ho1/
As you can see in the example above, the row--full-width element bleeds out of the container, and it aligns with the header even when there's a scrollbar.
Tested on Edge 18 (Win), Edge 88 (Win/Mac), and Chrome 88 (Win/Mac).
The easiest way is set the html & body to 100vw:
html, body{ width:100vw; overflow-x: hidden; overflow-y: auto; margin: 0; }
The only problem is the right-most will be cut a little if scrollbar is shown.
If the case were something similar to a slider:
As posted in many answers, width 100% doesn't take into account the scrollbar, while 100vw does. In the case of having many elements that need to take the width of the window and that are nested inside a container already with 100% window width (or whose natural block width would be such), you can use:
Display flex for container
Flex: 0 0 100% for child elements
It's not my solution, but helps me create dropdown fullwidth menu with absolute in relative element in not fullwith span.
We should get scroll with in css var in :root and then use it.
:root{
--scrollbar-width: calc(100vw - 100%);
}
div { margin-right: var(--scrollbar-width); }
https://codepen.io/superkoders/pen/NwWyee

Width transitioning from fixed size to "auto" using CSS without Javascript

I'm having a bit of a problem making script-less CSS-only animated transition of an element that's initially set to a fixed width and should expand on mouse over to auto width according to content in it. When mouse goes out it should collapse back to fixed width.
Let's say I have a menu:
<menu>
<li>Item</li>
<li>Item with long text</li>
</menu>
Initially it would display as a collapsed 50px wide vertical bar with icons only. When one mouses over it reveals icon labels.
This is a simplified example of what I'm trying to achieve. First menu is the one that needs to transition and second one is there just to show what auto width should be for this amount of content.
Problem
This is just part of the whole CSS that plays an important role here:
menu {
width: 50px;
}
menu:hover {
width: auto; /* setting to other fixed width works as expected */
}
The problem is that when you set width to auto one of the two will happen:
Browser animates from fixed width 0 (Chrome) - if we then add min-width it does next one
Browser doesn't animate anything just applies new style
You can make use of the max-width trick, it's not perfect, but it gets around the problems with transitioning a numeric value to a string state:
http://jsfiddle.net/Cqmuf/1/
(the above has been updated with float:left)
The downside to this method is that you have to set a max width for your menu, but then I usually find that this is a good thing to do anyway.
markup:
<div class="menu">
[i] Hello
[i] There
</div>
css:
div a {
display: block;
overflow: hidden;
white-space: nowrap;
}
.menu {
transition: all 2s;
max-width: 17px;
/*
here you can set float:left or position:absolute
which will force the menu to collapse to it's minimum
width which, when expanded, will be the actual menu
item widths and not max-width.
*/
float: left;
}
.menu:hover {
/*
If you have the possibility of varied widths, i.e. multilingual
then make sure you use a max-width that works for them all. Either
that or do what a number of multilingual sites do and set a body
class that states the current language, from there you can then
tailor your max-width differently e.g. wider for German.
*/
max-width: 300px;
}
Example of seperate dimensions for multilingual:
.lang-de .menu:hover { max-width: 400px; }
.lang-gb .menu:hover { max-width: 300px; }
So instead of transitioning the width, you are actually modifying the max-width property, which you can set a fixed value to more easily, all because it will only come into use when this limit has been reached, and remains invisible until then.

CSS - text overflow: display 2 lines of text only

Given a long string of content, I want to display just the first 2 lines of text. The container of this content is fluid and will resize to the browser's width. Regardless of the container's width, I want the text to always only show 2 lines. Is there a way to do this?
If there is no way to do the above, is there a way to restrict based on number of characters?
This snippet will help you.
Just Adjust Max-Height and Line-height for the change in font size.
.limit-2 {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
line-height: 21px;
max-height: 48px;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
A pure CSS solution would imply the use of a stated height for the text block and the ´text-overflow`property. This is rather difficult to achieve because CSS has no notion of lines. A JavaScript solution, instead, would imply a regular expression matching for the newline character.
I have idea of some workaround to solve your problem (couse I think this is not possible by css).
So, my solution works as follow:
var height = parseInt($("#foo").css("line-height"));
var lineCount = 2;
height *= lineCount;
$("#foo").css("height", height + "px");
You take line-height of your container and set height of container to line-height * lineCount.
jsFiddle
You can fix the height of the div and make overflow as hidden
div{
height:auto;
max-height:40px;
overflow:hidden;
background:red
}
DEMO UPDATED
OR
Use simple Jquery method
$('p').condense({ellipsis:'…', condensedLength: 55});
DEMO 2

Resources