I was always under the impression that CSS transforms are purely visual and don't influence the actual site layout in any way. I recently found a weird bug in my project, where an image which is transformed out of the viewport (to later fly in) changed the width of the page.
My coworkers and I are extremely confused by this. I provided an example below (also hosted here: https://transform-confusion.netlify.app/) that shows the phenomenon with a transform: scale(2) applied to a div completely changing the width of the browser.
Do I just misunderstand how CSS works, is this a weird browser bug or what is going on here?
Source code used here and in the video:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
body {
border: 2px dotted black;
}
.scale {
transform: scale(2);
transform-origin: top left;
}
.scalable{
background: lightblue;
padding: 5px;
}
</style>
</head>
<body>
<h1>This is a test</h1>
<button>Toggle transform</button>
<div class="scalable">
Why does the Site start scrolling horizonally from a simple transform?
<pre>transform: scale(2)</pre>
</div>
<script>
const scalable = document.querySelector(".scalable");
document.querySelector("button").addEventListener("click", () => {
scalable.classList.toggle("scale");
})
</script>
</body>
</html>
I was always under the impression, that CSS transforms are purely visual and don't influence the actual site layouting in any way
This is correct since transformation will not affect the position of any other element and the layout will remain the same.
What you are facing is related to overflow:
The scrollable overflow of a box is the set of things extending outside of that box’s padding edge for which a scrolling mechanism needs to be provided.
The scrollable overflow area is the non-rectangular region occupied by the scrollable overflow, and the scrollable overflow rectangle is the minimal rectangle whose axes are aligned to the box’s axes and that contains the scrollable overflow area.
.. the border boxes of all boxes for which it is the containing block and whose border boxes are positioned not wholly outside its block-start or inline-start padding edges, accounting for transforms by projecting each box onto the plane of the element that establishes its 3D rendering context. ref
So transform is a part of the Scrollable Overflow
You will find that some properties are part of the Ink Overflow and this one doesn't generate a scroll:
The ink overflow of a box is the part of that box and its contents that creates a visual effect outside of the box’s border box. Ink overflow is the overflow of painting effects defined to not affect layout or otherwise extend the scrollable overflow area, such as box shadows, border images, text decoration, overhanging glyphs (with negative side bearings, or with ascenders/descenders extending outside the em box), outlines, etc.
For example box-shadow will never create a scroll
.box {
width:100px;
height:100px;
background:red;
box-shadow:0 0 0 1000vmax blue;
}
<div class="box"></div>
I just found the important bit of info in the Specs. My misunderstanding was the overflow behaviour:
For elements whose layout is governed by the CSS box model, the transform property does not affect the flow of the content surrounding the transformed element. However, the extent of the overflow area takes into account transformed elements. This behavior is similar to what happens when elements are offset via relative positioning. Therefore, if the value of the overflow property is scroll or auto, scrollbars will appear as needed to see content that is transformed outside the visible area. Specifically, transforms can extend (but do not shrink) the size of the overflow area, which is computed as the union of the bounds of the elements before and after the application of transforms.
from https://drafts.csswg.org/css-transforms/#transform-property
Slightly late to the party, but here's my solution to the problem:
html {
overflow-x: hidden;
}
Probably a bit hacky, but it works for me!
Related
In simple terms I am trying to scale a large box (used when the browser is in full screen) down to a smaller box using the CSS transform property. The box is scaling properly but the browser is still showing the scroll bars as if it is not scaled. I do not want to turn off overflow, I am hoping I am missing something.
A fiddle of my issue. Notice the vertical scroll bar:
http://jsfiddle.net/adamlj/uvfhr8nw/4/
<html>
<head>
<style>
.scaleme {
background: red;
height: 2000px;
width: 4000px;
}
.scaler {
transform-origin: top left;
transform: scale(0.16666667);
}
</style>
</head>
<body>
<div class="scaler">
<div class="scaleme"></div>
</div>
</body>
</html>
transform leaves the original element untouched. It only affects how the element is rendered.
But the original element remains the same, hence occupying the same space in document flow. So the scrollbars will not go away unless you resize the element.
If you're looking for a solution to resize both the element and the space it occupies in document flow, have a look at this answer.
"use strict";var _createClass=function(){function e(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}return function(t,n,i){return n&&e(t.prototype,n),i&&e(t,i),t}}();function _classCallCheck(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var zoomFactor=function(){function e(t){_classCallCheck(this,e),this.el=this.q(t,document),this.b(),this.u()}return _createClass(e,[{key:"q",value:function(e){return(arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.el).querySelector(e)}},{key:"b",value:function(){var e=this.el.innerHTML,t=document.createElement("z-1"),n=document.createElement("z-2"),i=document.createElement("z-3"),l=document.createElement("style");this.el.innerHTML="",this.el.appendChild(t),t.appendChild(n),n.appendChild(i),i.innerHTML=e,l.appendChild(document.createTextNode("z-1,z-2,z-3,zoom-factor{display:block}z-1,zoom-factor{position:relative;overflow:hidden}z-1,z-2{width:100%}z-1,z-2,z-3{color:#fff}z-1{float:left;overflow:hidden}z-2{position:absolute}z-3{transform-origin:left top;width:0}")),document.getElementsByTagName("head")[0].appendChild(l)}},{key:"v",value:function(){return this.q("input")?this.q("input").value:parseFloat(this.el.dataset.scale)||1}},{key:"u",value:function(){var e=this.v(),n=this.q("z-1"),i=this.q("z-2"),l=this.q("z-3");n.style=i.style=l.style="",i.style.width=n.clientWidth*e+"px",l.style.transform="scale("+e+")",n.style.height=l.clientHeight*e+"px"}}]),e}();new zoomFactor("zoom-factor");
.scaleme {
background: red;
height: 2000px;
width: 4000px;
}
<zoom-factor data-scale="0.16666667">
<div class="scaleme"></div>
</zoom-factor>
Placed the CSS inside the JS, ran it through babel to make it es2015 compatible and minified it. Once you place that js in your page, it will automatically parse the <zoom-factor> element according to its data-scale.
OK here's a challenging one! I really want to see if this can be accomplished with CSS only if possible.
I have a unique layout that requires images to be sized and positioned based on their parent container. If the image exceeds it's parent in height or width I need it to reduce size to fit. At the moment I'm using max-width and max-height together and it's working fine. The image resizes to fit and of course keeps it's aspect ratio.
Now here's the tricky part. I need to add a special shadow to this image that cannot be accomplished with CSS box shadows. The shadow uses PNG image. The shadow needs to be sized and positioned in relation to the image - meaning it falls at the bottom of the image and it equals the width of the image.
Normally I would achieve the shadow with ::after and size and position it relative to it's parent element, which works perfectly Except it's parent is the image and images do not allow ::before or ::after.
So as far as I can tell, the only way to achieve this is to wrap the image in another container so I can use that container as the parent elements and positioning reference for the shadow layer. But I cannot find a way to make that container div behave in the same way as the image in terms of the max-width and max-height sizing while still maintaining it's aspect ratio.
The best methods for maintaining aspect ratios use padding top, which works brilliantly when the width is the only important factor. But the padding-top technique doesn't allow for the container to have a max-height.
So I'm looking for a CSS technique that will allow a block element to maintain its aspect ratio, and have max-width and max-height at the same time. Similar to how an image would behave in this situation.
I've scoured the internets for a solution and haven't seen anyone describe this exact situation. Would be extremely grateful to anyone who can assist.
Added 1 Sept 2017:
I should mention that it's more than just the shadow I need to position relative to the image. There are some other elements as well that need to be positioned in this way, and those other elements are not simple background images. So while Lightbender's solution is great for the shadow, it doesn't solve the bigger issue at hand. I need a container around the image that I can use as reference to position other child elements.
While before and after won't work (easily) but you can still use padding and a background image and it will work exactly the way your current setup works.
img.fancyshadow {
height: auto;
width: auto;
max-width: 100%;
max-height: 100%;
padding: 0 10px 10px 0; /* adjust as needed */
background: url('path/to/your/shadow');
box-sizing: border-box;
}
I don't have a Mac handy, so I've only tested this in Firefox, Chrome, and IE, can anyone confirm Safari as well?
So I would like to see a simple example of what you are attempting as a starting point but you mentioned that the images need to be sized/positioned based upon the size of their container.
Here is an starting example of something like that. Not sure if it can be modified to suit your issue. Let me know and I can tweak.
When needing to have responsive images, I never use IMG tags. Setting the background image in CSS provides much more control on responsive sites/apps.
Documentation on background-size:
cover Scale the background image to be as large as possible so that the background area is completely covered by the background image. Some parts of the background image may not be in view within the background positioning area
contain Scale the image to the largest size such that both its width
and its height can fit inside the content area
$(function() {
$('.banner').resizable();
});
.banner {
background-image: url('https://s-media-cache-ak0.pinimg.com/originals/15/ae/a6/15aea601612443d5bddd0df945af6ffd.jpg');
background-size: cover;
background-position: center;
height: 175px;
width: 100%;
}
p {
color: #666;
}
.ui-resizable-se {
box-shadow: -1px -3px 10px 3px white;
}
<link href="https://code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>
<h1>Resize the image using the black triangle at bottom right of image</h1>
<p>Note how the image fills the container and the position is always centered (you can control where the position is, doesn't have to be in the center)</p>
<div class="banner">
</div>
I'm trying to create html buttons/labels with text and initially zero width (after insertion, they'll be stretched to be visible, but I want this transition to be smooth). I've tried modifying the initial width and min-width style properties, but no luck.
The following is what I want to work.
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.zerowidth{width: 0px;min-width: 0px;}
.nonzerowidth{font-size: 30px;}
button{font-size: 30px;}
label{font-size: 30px;}
span{font-size: 0px;}
</style>
</head>
<body>
<span>
<button>This should be visible</button>
<button class='zerowidth'>I want this to be invisible</button>
<label class='zerowidth'>same here</label>
<button>c</button>
</span>
</body>
</html>
I want the above to appear identical to a version without the .zerowidth elements, such that the .zerowidth elements can have their widths animated to nonzero values later.
If there's an easier way to insert an item into the DOM that allows for smooth repositioning of the elements around it (the above is a toy example - in practice I'll be inserting elements programatically from Dart), I'd be happy to hear that too (though I'd rather stay away from absolutely positioning everything if possible).
Thanks.
To make a button element really zero-width as regards to total width occupied (not just content width, which is what the CSS property width specifies), you need to set horizontal padding and vertical borders to zero. Moreover, to prevent browsers from dividing the content into several lines, you need to prevent line breaks. And you need to make overflowing content hidden; for an element with zero content width, any content overflows, of course. You would thus add the following:
.zerowidth {
padding-left: 0; padding-right: 0;
border-left-width: 0; border-right-width: 0;
white-space: nowrap;
overflow: hidden;
}
For some reason, this is not sufficient for making a label element zero-width (tested on IE, Chrome, Firefox). On the other hand, a label element is for specifying labels of form fields and other labellable elements, and you get nothing but trouble by trying to use it for other content. So consider using e.g. span instead.
<button class='zerowidth'>I want this to be invisible</button>
<label class='zerowidth'>same here</label>
if you want these two things invisible, why not add style="display:none;" to both. And if you want to display them, do a hover effect and display:block; to that element.
I want to make a header like http://www.chacha.com (doesn't move, is about that wide and that height, and able to fit divs inside it and also has to be an image)
I am starting off with a blank html document and a blank css page, so there I haven't currently written any code.
I've been trying two days straight to do this now so I would really appreciate any help anyone can provide.
I have gimp so if anyone could also give me image dimensions for a perfect header and perfect background size I would appreciate it even more.
CSS:
#header {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 10px;
background: url(yourimage.png) repeat-x;
}
<!--html -->
<div id="header"></div>
That should give you a starting place, I can't tell you more without seeing exactly what the layout's supposed to be.
The CSS property you're looking for is position: fixed which will position the element relative to the viewport. This is good breakdown of positioning: https://developer.mozilla.org/en-US/docs/CSS/position
In this specific case, what you've got is an element with styles roughly along these lines:
#header_id {
position: fixed;
width: 100%;
height: 35px;
}
You don't have to set the height, but unless there is content in the fixed element, it will collapse if there is no height specified. They also appear to have put a drop-shadow on the element toget the neat floating effect.
If you want to have an image inside, you can just put the <img> inside the header element, or use it as the background-image url in the CSS and position it with background-position (see also: https://developer.mozilla.org/en-US/docs/CSS/background-position although the compatability table at the bottom is important if you want to do anything too specific with this property).
You can do this with any block-level element (or any element with display:block set on it). In your example they are using the HTML5 <header> tag; a <div> would work, too, if <header> wasn't appropriate for your page.
I would recommend using the Firebug addon with Firefox (or similar developer consoles with other modern browsers) -- you can right click on an element on the page and select 'Inspect element' from the dropdown menu and get a breakdown of both the markup and styling to see how other websites are constructed. Very useful for when you're browsing the internet and you see something and think, 'that's a neat trick, how does it work?'
FOR FULL WIDTH FIXED HEADER
header {
width:100%;
background:green;
height:60px;
margin:-8px;
position:fixed;
}
FOR NONFULL WIDTH FIXED HEADER
Create a div and set width and height (you can also set it left or right by float:left, float:right)
then in this div put the code above but without margin:-8px; and change the width to the width that your div has.
Here is a test
I building an iPhone webb app based on iWebKit's framework. I'm currently integrating a CSS div slider to improve the navigation between pages (divs) and everything works fine except one thing.
It appears as if I'm supposed to set a fixed height value to the div containing the sliding objects. These objects will contain quite a lot of content and wary in size, hence the divs/page have to expand vertically by default. For some reason, it appears as if the browser interprets min-height as height and doesn't expand the div to display all content. I realize there's probably a small mistake somewhere, something I have forgotten to add or remove. Please help me by pointing these out for me. Thanks
Wrapper containing the slides:
#contentWrapper {
float: left;
min-height:305px;
position: relative;
margin:0px;
padding-bottom:0px;
display:block !important;
}
Class added on every sliding obj
.additional-block {
height:auto;
position: absolute;
padding-bottom:30px;
display:block !important;
}
Live demo: http://utvecklingspunkten.se/heightIssue.php
Click on "Click" to see the actual issue appearing; the text is cut off below 305 px. The issue appears in all browsers including Safari for iPhone.
It's the overflow: hidden on your "content2" div that's conflicting here. Setting that means that the container can overflow, so it does. (Removing the min-height will show you that it would be 0 pixels high otherwise.)
Removing the overflow setting will have the effect I think you want.