Changing the width of an element with a "step" of several pixels
I’ll make a reservation in advance that, unfortunately, you can’t do pure CSS here: Firefox and IE8 + are too accurate (yes, in this case it’s bad) they calculate the width of the blocks. However, for these browsers a simple script is added in a couple of lines, if you still need to achieve the ideal, although this spoils all the charm.
For those who are too lazy to read everything, a link to the final version: jsfiddle.net/XeFTr/11
The bottom line is that sometimes it is necessary to display a rubber block with several child elements on the page, and the child must either be visible in its entirety or not visible at all. The simplest example is a ribbon of some pictures with scrolling and arrows on the sides. The image cropped from the side looks very ugly looking from under "overflow: hidden". The way out is to make the width of the wrapper always be a multiple of how many pixels.
Of course, if the width of the descendants varies from element to element, then this method does not make sense.
The principle of operation is that most browsers calculate the width of the child, if it is specified in percentage, based on the width of the parent. Thus, if the element has a width of 1000%, then when the width of its parent changes by a pixel, the element itself will be stretched or compressed by 10 pixels. Actually, that's all.
This thing was described in the Lebedev screencast by Sergei Chikuyonok back in 2008, in the context of fixing a bug with “jumping” blocks in IE6. However, to my surprise, I did not find a description of this fun technology on Habré.
I will consider a spherical element in a vacuum, initially consisting solely of a block with "overflow: hidden" and a very wide element with a bunch of descendants whose total width will exceed the width of the wrapper:
Live example: jsfiddle.net/KKSCe/4
So, we have a list of elements, the width of each (including margin) is 150px. Obviously, the last element of the list will be visible in its entirety in one case out of 150. Well, in 11 cases: you can leave the five-pixel right margin out of visibility and accept that the left margin is part of the previous element. :-)
From the description above it is clear that you need to add a wrapper to the ".wrapper" block, and to set the ".wrapper" itself to a width of 150 times greater than the width of this wrapper. It is also clear that if you write “width: 15000%” without additional manipulations, the result will be somewhat not what you want. It is necessary that “.wrapper” maintain a width of 60% of the screen width. This is compensated by the small width of the wrapper, calculated by proportion.
15000/100 = 60 / x
x = 0.4
We write this:
Live example (look in Chrome or something): jsfiddle.net/XeFTr/9
You can stretch the viewing area there and see how cool everything works. The block does accept widths that are multiples of 150px.
It would seem great!
The problem is that in some cases, as in this example, you have to set the width of the wrapper to less than one percent - when the step should be more than 100px. Most browsers swallow it and consider everything right. Even IE6. But with the Opera, including the latest versions, it’s a disaster. Opera does not understand values less than 1%.
The solution is quite simple, but requires more HTML. Instead of making one block with a width of 0.4%, we will do two. The width of the first is 1%, the width of the second nested in the first is 40%. Here, Opera becomes a consonant.
Live example: jsfiddle.net/XeFTr/8
It is quite possible that using a value of less than 1% is fraught sideways in other browsers, but I have not seen this. Correct me if I am wrong.
However, in any case, a fractional percentage value is not particularly good.
So, now everything works everywhere except for Firefox and the latest versions of IE. Let's fix it too.
For simplicity, I will use jQuery. The function, as well as the markup, is also for laboratory conditions.
Final Version: jsfiddle.net/XeFTr/11
In examples closer to life, there are all kinds of arrows, frames and the like. Their positioning (in particular, the positioning of elements from the side where the block is stretched) is done in exactly the same way.
I checked the performance in Firefox, Opera, Chrome, IE6-9 and Safari for Windows.
It would be great to see all this under various Safari under MacOS, Konqueror, etc.
I will also be glad to comments / corrections.
The writing is based on the screencast mentioned above and subsequent independent research and experiments on this topic.
For those who are too lazy to read everything, a link to the final version: jsfiddle.net/XeFTr/11
What is the point?
The bottom line is that sometimes it is necessary to display a rubber block with several child elements on the page, and the child must either be visible in its entirety or not visible at all. The simplest example is a ribbon of some pictures with scrolling and arrows on the sides. The image cropped from the side looks very ugly looking from under "overflow: hidden". The way out is to make the width of the wrapper always be a multiple of how many pixels.
Of course, if the width of the descendants varies from element to element, then this method does not make sense.
How it works?
The principle of operation is that most browsers calculate the width of the child, if it is specified in percentage, based on the width of the parent. Thus, if the element has a width of 1000%, then when the width of its parent changes by a pixel, the element itself will be stretched or compressed by 10 pixels. Actually, that's all.
This thing was described in the Lebedev screencast by Sergei Chikuyonok back in 2008, in the context of fixing a bug with “jumping” blocks in IE6. However, to my surprise, I did not find a description of this fun technology on Habré.
How to write this?
I will consider a spherical element in a vacuum, initially consisting solely of a block with "overflow: hidden" and a very wide element with a bunch of descendants whose total width will exceed the width of the wrapper:
.wrapper {
border: 1px solid black;
margin: 10px;
overflow: hidden;
width: 60%; /* пусть лента будет чуть шире половины экрана */
}
.itemList {
height: 90px;
list-style: none;
margin: 0;
padding: 0;
width: 10000%; /* просто чтобы всё было в одну строку */
}
.item {
background: green; /* картинок не будет, будет цвет блоков */
display: inline; /* ну да, грязный хак для IE6 */
float: left;
height: 80px;
margin: 5px;
padding: 0;
width: 140px;
}
Live example: jsfiddle.net/KKSCe/4
So, we have a list of elements, the width of each (including margin) is 150px. Obviously, the last element of the list will be visible in its entirety in one case out of 150. Well, in 11 cases: you can leave the five-pixel right margin out of visibility and accept that the left margin is part of the previous element. :-)
From the description above it is clear that you need to add a wrapper to the ".wrapper" block, and to set the ".wrapper" itself to a width of 150 times greater than the width of this wrapper. It is also clear that if you write “width: 15000%” without additional manipulations, the result will be somewhat not what you want. It is necessary that “.wrapper” maintain a width of 60% of the screen width. This is compensated by the small width of the wrapper, calculated by proportion.
15000/100 = 60 / x
x = 0.4
We write this:
.......pre-wrapper {
margin: 10px;
width: 0.4%;
}
.wrapper {
border: 1px solid black;
overflow: hidden;
width: 15000%;
}
Live example (look in Chrome or something): jsfiddle.net/XeFTr/9
You can stretch the viewing area there and see how cool everything works. The block does accept widths that are multiples of 150px.
It would seem great!
But it's not so smooth
The problem is that in some cases, as in this example, you have to set the width of the wrapper to less than one percent - when the step should be more than 100px. Most browsers swallow it and consider everything right. Even IE6. But with the Opera, including the latest versions, it’s a disaster. Opera does not understand values less than 1%.
The solution is quite simple, but requires more HTML. Instead of making one block with a width of 0.4%, we will do two. The width of the first is 1%, the width of the second nested in the first is 40%. Here, Opera becomes a consonant.
.......pre-wrapper {
margin: 10px;
width: 1%;
}
.opera-fix {
width: 40%;
}
Live example: jsfiddle.net/XeFTr/8
It is quite possible that using a value of less than 1% is fraught sideways in other browsers, but I have not seen this. Correct me if I am wrong.
However, in any case, a fractional percentage value is not particularly good.
So, now everything works everywhere except for Firefox and the latest versions of IE. Let's fix it too.
Script for overly accurate browsers
For simplicity, I will use jQuery. The function, as well as the markup, is also for laboratory conditions.
$(function() {
if ( $.browser.mozilla || ($.browser.msie && $.browser.version >= 8) ) {
resizeFix();
$(window).resize(resizeFix);
}
});
function resizeFix() {
var w,
h = $('.wrapper'),
i = $('.item').outerWidth() + parseInt($('.item').css('marginLeft')) * 2;
h.width('');
w = Math.floor(h.width() / i) * i;
h.width(w + 'px');
}
Final Version: jsfiddle.net/XeFTr/11
I summarize
In examples closer to life, there are all kinds of arrows, frames and the like. Their positioning (in particular, the positioning of elements from the side where the block is stretched) is done in exactly the same way.
I checked the performance in Firefox, Opera, Chrome, IE6-9 and Safari for Windows.
It would be great to see all this under various Safari under MacOS, Konqueror, etc.
I will also be glad to comments / corrections.
The writing is based on the screencast mentioned above and subsequent independent research and experiments on this topic.