Background
I have a small one-level CSS flyout menu (well, technically it's an expanding element). It is absolutely positioned at the bottom left of a parent absolutely-positioned element that is fairly narrow. See the second h1 element below:
<div id="controls">
<h1>Controls 1</h1>
<h1 id="size" class="poplinks button">
Size
<img src="">
<img src="">
<img src="">
</h1>
</div>
This is very simply turned into an expanding menu/flyout like so:
.poplinks:hover {
width:auto;
}
.poplinks a {
display:none;
}
.poplinks:hover a {
display:inline-block;
}
Problem
This results in the following button-like element:
The h1 has style width:48px;, and there is also a style rule to apply width:auto; to the h1 element upon hover, so it should be able to widen. However, upon hovering, the submenu is being forced to stay no wider than the parent element's width, when I'd like it to extend to the right (out of the parent's containing box).
What I want to see (obtained by moving the element outside the parent, but I would like it to remain inside for inheriting styling and so when I move the menu bar from the left to the top, it follows automatically):
Is this possible? Do you have any recommendations?
See this in action for yourself in a JS Fiddle.
Browsers
Note: I plan for this to work in Firefox, Chrome, and IE 8. I am doing the main styling in Firefox & Chrome and when basically done, will add conditional CSS to get IE to work right and look as close as I can.
Rationale
The reason I am positioning the parent menu absolutely is that I'm building an application-like page for displaying images. The page will be hosted within a parent Windows application and doesn't need a lot of identifying information: just to display the desired images. I chose to make the menu absolutely positioned rather than using inline-block or floats or some other method to get my menu columns into place (there are two). However, it doesn't have to be this way. If you have a suggestion for an alternate layout or strategy, I am all ears.
First, your #controls need overflow:visible. Then, #size should be given an explicit left instead of right. And finally, .poplinks needs white-space: nowrap to prevent the wrap.
http://jsfiddle.net/VaWeK/11/
I'm writing this answer because I might need it again in the future.
Although I've found this in the selected answer, it also mentions lots of other details asked by OP, and that somehow ended up hiding what was the important part to solve my problem.
What did the trick for me was the: white-space: nowrap.
I needed this for a dropdown component, which I think is a common use case.
The code below uses React and styled-components.
const styled = window.styled;
const LS = {};
LS.DropdownButton_DIV = styled.div`
position: relative;
width: 150px;
height: 30px;
display: flex;
align-items: center;
padding-left: 8px;
border: 1px solid silver;
user-select: none;
cursor: pointer;
margin-bottom: 100px;
`;
LS.Dropdown_DIV = styled.div`
position: absolute;
left: 0;
top: 100%;
display: ${props => props.open ? "block" : "none"};
`;
LS.DropdownItem_DIV = styled.div`
display: flex;
padding-left: 8px;
border: 1px solid silver;
/* THIS IS WHAT SOLVES THE PROBLEM */
white-space: ${props => props.noWrap ? "nowrap" : "initial"};
`;
function App() {
const [open1,setOpen1] = React.useState(false);
const [open2,setOpen2] = React.useState(false);
function toggleDropdown1() {
setOpen1((prevState) => !prevState);
}
function toggleDropdown2() {
setOpen2((prevState) => !prevState);
}
return(
<React.Fragment>
<LS.DropdownButton_DIV onClick={toggleDropdown1}>
Dropdown Button 1
<LS.Dropdown_DIV open={open1}>
<LS.DropdownItem_DIV>
Dropdown Item Longer Than Parent
</LS.DropdownItem_DIV>
</LS.Dropdown_DIV>
</LS.DropdownButton_DIV>
<LS.DropdownButton_DIV onClick={toggleDropdown2}>
Dropdown Button 2
<LS.Dropdown_DIV open={open2}>
<LS.DropdownItem_DIV noWrap={true}>
Dropdown Item Longer Than Parent
</LS.DropdownItem_DIV>
</LS.Dropdown_DIV>
</LS.DropdownButton_DIV>
</React.Fragment>
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
* {
box-sizing: border-box;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<script src="//unpkg.com/styled-components#4.0.1/dist/styled-components.min.js"></script>
<div id="root"/>
Two ways: you can either hardcode an absolutely defined width, or you can do a relative width that is greater than 100%. Width of 100% will be 100% of the containing div (as long as that div has a defined width). If this div has an absolute width, meaning it is defined with pixels instead of percentages or ems, then you can simply make the hover css wider than the parent div. For example, if your parent div is 100px wide, the hover on the child should be 200px wide or 200% or something like that.
Related
I marked this question with "react" tag, however, I think that framework does not matter so much in this case.
I have a component, whose job is to display some children. All this component does is it displays some styling - a border and a background - so it's just a box.
This box could be displayed "dynamically", so no static height would be specified - it could be "flex-grow: 1" for example.
How to make the children fill this component entirely?
This is my Box:
class Box extends Component {
render() {
return (
<div className={"box " + this.props.className}>
<h2>BOX</h2>
<div>{this.props.children}</div>
</div>
);
}
}
Here are its styles:
.box {
border-width: 5px;
border-color: black;
border-style: solid;
background-color: purple;
}
Ideally, child would just set "height: 100%", but this will not work, because Box does not define any height.
One solution would be to set "display:flex" on the div inside of which "{this.props.children}" is placed. However, this requires children to use "flex-grow: 1" - I think it's not best solution, because children shouldn't know how Box is implemented. It also makes the problem recursive - children of my children again need to use flex-grow...
//EDIT
Here's an example: https://codesandbox.io/s/nervous-sunset-bvhjh
Box-content should fill the whole place that it has in a Box, but it does not. How to achieve that?
You can achieve that in pure css (this way, children do not need to be aware of the flex mode of their parents) :
.row2 {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.row2 > div {
flex-grow: 1;
}
by styling all the child divs.
a codesandbox to see the result
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;
}
Not sure if this is even remotely possible, but asking anyway. I have a div with a fixed height and width that I cannot edit. I can, however, add in child elements. Is there a way to only use child elements to shrink the size of the parent?
And I can only use inline css, with no js or jquery.
Since the original div is empty (see comments), instead of setting the default fixed width/height on all states of the div - you could set these properties only for the empty state of the div using the empty pseudo class.
This way, when the div gets children added - those original fixed widths don't apply anymore.
div:empty {
width: 200px;
height: 200px;
}
div {
border: 2px solid green;
}
<div></div>
<hr>
<div>
<p>some text</p></div>
I am trying to set up a form that displays a vertical separator between two elements that appear side by side. These are the problem parameters:
The height of either element is unknown and will change by virtue of the contents being modified with JavaScript in response to user interaction.
The separator should cover the whole of the elements' shared vertical border, irrespective of which element happens to be taller at any given time.
Given the above it seems that this setup will do the trick:
<div>This is some text on top.</div>
<ol>
<li id="a">Lalalala</li>
<li id="b">Lololol</li>
</ol>
<div>And some text on the bottom.</div>
CSS
ol { overflow: hidden }
li { float: left; width: 5em; padding: 4px }
div { clear: both }
#a { background: gold; min-height: 100px }
#b { background: yellow; border-left: 1px black dotted }
#b { padding-bottom: 400px; margin-bottom: -400px } /* "infinitely" tall */
The idea is that the second element becomes "infinitely tall" by applying bottom padding and gets a left border; elements following the group are brought back into their original position by counteracting the padding with negative bottom margin; and the "unused" portion of the vertical border is hidden by giving the parent overflow: hidden.
This setup indeed works correctly (JsFiddle) on Firefox, Chrome and IE >=8 (my compatibility requirements):
However, when I try to apply the same technique in my real HTML Firefox breaks down and seems to not honor the overflow: hidden set on the parent element. As a result the infinitely tall vertical border bleeds through all elements following the two panels on the page.
Here is a JSFiddle of (simplified) real copy/pasted content together with my actual CSS rules that shows the problem. Note that only Firefox mishandles this; other browsers continue to display it properly.
Correct render:
Firefox render:
I am properly stumped: why would Firefox display the proof of concept correctly and botch the real deal? And how can I fix it?
I was able to fix your JSFiddle by changing the fieldset element to a div or by surrounding the fieldset with a div that had overflow set to hidden. Maybe worth a try. Is the fieldset tag essential to your HTML?
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.