How to work around Firefox's miscalculation of widths - html

Under some situations, Firefox grossly miscalculates the widths of some DOM elements, which in turn causes layouts to break.
This jsFiddle gives an example of the problem. The numbers displayed below the table are the widths (in pixels) of the div that is shaded dark-gray, and of its parent (as reported by jQuery). Compare the results produced by the latest versions of Firefox (or IE 11) and Chrome (or Safari). Chrome always reports 250 for both widths (as expected), but Firefox always reports a larger number (though the exact number may depend on the OS and/or version of FF and/or phase of the moon). As a result, there's not enough room to render the svg elements in the next td at 3/row.
(More bewildering still: the numbers displayed below the table will vary according to the number of svg elements included in the second td element.)
This erratic/unpredictable behavior makes it practically impossible to design a layout.
How can I ensure that FF will compute such widths correctly, or alternatively, how can I work around this bug?
EDIT: updated jsFiddle (including the link to it).
Now, to keep the gods of SO happy:
body > div,table,table *{outline:1px solid red;}
html,body{height:100%;}
*{
-webkit-box-sizing:border-box;
-moz-box-sizing:border-box;
box-sizing:border-box;
}
*{margin:0;padding:0;border:0;}
table{
border-spacing:0;
border-collapse:collapse;
}
body{
font-family:courier;
font-size:13px;
background-color:palegoldenrod;
}
body > div{
width:312px;
margin:0 auto;
padding:40px 0 0;
background-color:white;
min-height:100%;
}
label{
display:block;
padding:0 1ex;
}
.button-container{
color:white;
background-color:#555;
}
.button-container > div{
display:inline-block;
}
.button-container > div:first-child{
font-weight:bold;
}
.ul-container > div{
width:100%;
border:1px solid black;
-webkit-border-radius:4px;
border-radius:4px;
}
ul{list-style:none;}
li{
width: 72px;
float:left;
margin: 0px 5px 1px;
padding: 0px 5px;
border-width: 1px;
line-height: 14px;
}
br{
clear:left;
}
body > div > div:last-child{margin:40px;}
<body>
<div>
<table><tbody><tr>
<td>
<div class="button-container">
<div>xxxx xxxxx</div>
<div>
<label> <input type="radio"> xxxx xxxx xxx xxxx xxxx </label>
</div>
</div>
<div class="ul-container" style="width: 250px;">
<div style="width: 248px;">
<ul style="width: 246px;">
<li>A</li><li>B</li><li>C</li><li>D</li><li>E</li><li>F</li><li>G</li><li>H</li><li>I</li>
</ul>
<br>
</div>
</div>
</td>
<td>
<div>
<svg width="20" height="20"></svg><svg width="20" height="20"></svg><svg width="20" height="20"></svg><svg width="20" height="20"></svg><svg width="20" height="20"></svg><svg width="20" height="20"></svg><svg width="20" height="20"></svg>
</div>
</td>
</tr></tbody></table>
<div></div>
</div>
(function ($) {
var $msg = $('body > div > div:last-child');
function showw (sel) {
var w = $(sel).width();
$('<span>', {text: '' + w})
.css('margin-left', 5)
.appendTo($msg);
console.log(w);
}
showw('.button-container');
showw('table td:first-child');
}(jQuery));

If you remove the inline-block rule for .button-container > div, it forces the two divs to sit on separate lines, allowing the table to take on a consistent width.
What is happening there is the browser is trying to put the elements on the same line. A cell in a table with no explicit width or overflow instructions will grow wider to accommodate the content lines inside it. Because the two elements are inline, they are considered a single line. The text wraps as you would expect (the browser does a good job protecting the integrity of your content), but this is arbitrary as far as the width calculation goes; it affects the calculated width of the line that the elements form, and therefore pushes the table cell open wider. The browser is trying to take what you gave it and make sense of it while it also tries to preserve the integrity and legibility of your data, because it is a table and that's what tables do.
This is another good example of why tables are not the right tool for layout. They are designed to accommodate and present data, so they do a good job flowing and sizing around text. Different user agents have different strategies when it comes to how this is accomplished -- that is all within the specification. When you abuse the element, you wind up having to contend with design properties that do not suit your use case.
Fiddle: http://jsfiddle.net/Cy7dA/

It isn't "miscalculating the width" at all.
Your problem is simply that your label and input are different widths in both browsers. The extra width is then pushing the button-container and td out further.
Try to get your input/label combo consistent between the browsers (you probably need explicit margins on the input) and your problem is solved.

Related

Incorrect percentage "padded" columns width when the table-layout is fixed

I'm trying to use percentage columns (that have some paddings) with the table-layout fixed. When I do that for regular divs, everything works as expected (box-sizing in divs is content-box). All paddings are subtracted from 500px, then the rest is split in a half, so div's widths are 150px;
<div style="display:flex;flex-direction:row;width:500px">
<div style="width:50%;padding:0px">ADSD</div>
<div style="width:50%;padding:0px 100px">SADDS</div>
</div>
But when I do the same for a table everything works different:
<table style="width:500px;table-layout:fixed">
<tr>
<td style="width:50%;padding:0px">ADSD</td>
<td style="width:50%;padding:0px 100px">SADDS</td>
</tr>
</table>
Cells get pretty odd widths (175.812 vs 118.188). It seems that paddings are considered, but god knows how. So the question is: how are paddings are considered for percentage columns when the table layout is fixed?
There was a question about table paddings before, but it was about the table padding itself, and not its cells paddings (so it's not relevant, border-spacing doesn't help, etc.)
*{box-sizing:border-box} That's all you need my friend.
Elements don't bound to their box. We need to give them box-sizing css for to behave properly. I hope this helps.
*{box-sizing:border-box}
Flex
<div style="display:flex;flex-direction:row;width:500px">
<div style="width:50%;padding:0px">ADSD</div>
<div style="width:50%;padding:0px 100px">SADDS</div>
</div>
<br>
Table
<table style="width:500px;table-layout:fixed">
<tr>
<td style="width:50%;padding:0px">ADSD</td>
<td style="width:50%;padding:0px 100px">SADDS</td>
</tr>
</table>
My whole answer was based on results obtained on Firefox 102.0 for Windows. It was found out later that given the same code, the result it's different on Chrome for example. So my answer doesn't tell the whole story.
And here a github issue with a detailed conversation and headaches around the table-layout:fixed feature. But it seems to me that the rules are vague enough for each vendor to interpret a given edge case in a different way. Anyway details like those found in that link, may help reading out why the sizes were computed differently in a different browser.
The table has a fixed width given as width:500px; and the width of the available space for the two columns get calculated as fullWidth - padding because appearently it first takes into account the fixed sizes. I actually don't have a clear reference for such statement, but it just comes out from trying. Reading around I found out it's raccomended not to use % width for columns when having table-layout: fixed; but that's another story.
The question remains: how the width values get calculated in this scenario?
Since the padding: 0px 100px gets exploded in padding-left: 100px; padding-right: 100px; the size of the available width for columns content is:
500px - 100px - 100px = 300px
The width of each column gets calculated as 50% of that amount:
50% of 300px ~= 150px
Here's a demo showing off the width of the elements once rendered:
const table = document.querySelector('.table');
const firstColumn = document.querySelector('.table tr > td:nth-child(1)');
const secondColumn = document.querySelector('.table tr > td:nth-child(2)');
const secondColumnContent = secondColumn.querySelector('div');
console.log( 'Table width: ' + table.offsetWidth );
console.log( 'First Column width: ' + firstColumn.offsetWidth );
console.log( 'Second Column width: ' + secondColumn.offsetWidth );
console.log( 'Second Column width (no padding): ' + secondColumnContent.offsetWidth );
.table{
width:500px;
table-layout:fixed;
box-sizing: content-box;
border-collapse: collapse;
}
.table tr > td:nth-child(1){
width:50%;
padding:0px;
border: solid 1px green;
}
.table tr > td:nth-child(2){
width:50%;
padding:0px 100px;
border:solid 1px gray;
}
.table tr > td:nth-child(2) > div{
border:solid 1px blue;
height:1rem;
}
<table class="table">
<tr>
<td>1stCOL</td>
<td>
<div>
2ndCOL
</div>
</td>
</tr>
</table>
And here how it shows on Firefox 102.0 on my screen:
Compared to what happens on Chrome 103.0.5060.114:

Display one image above and one below with less padding

I want to display two images in a div with one at the top and one on the bottom.
I have achieved this but there seems to be extra space especially at the bottom and I don't know where this comes from. When I use Firebug layout it show a height of 61 pixels but my images are only 18x16 and I think that doesn't include padding and margins which are just a few pixels in any case.
What am I doing wrong? Is there a better way to do this?
jsfiddle
<div class="ex6">
<img src="images/uparrow.png" align="top" id="Z6Sync" width="18" height="16" title="up" onclick="manualup()" alt="up"><p>
<img src="images/downarrow.png" id="Z6Sync" width="18" height="16" title="up" onclick="manualup()" alt="down">
</div>
css
div.ex6
{
padding-top:5px;
padding-bottom:1px;
padding-right:10px;
padding-left:10px;
border:2px;
font-size:0.7em;
border-style:solid;
border-color:#ddd;
margin-top: 8px;
margin-bottom: 2px;
overflow: hidden;
float:right;
background: #eee;
cursor:pointer
}
Your problem lies in the <p> tag you added after the first image. A paragraph has a fixed style that includes a margin/padding after the paragraph. Get rid of it and take care of adding the line break via CSS to make sure the images are shown one below the other.
To achieve this you can for example set the images inside of your image to be displayed as block element:
div.ex6 img { display: block }
If you do that, you will have to add some more styling though to add some more margins, especially between the images. You do could do it like this:
div.ex6 img:first-child { margin-bottom: 5px }
But there are also many other ways, including just using <br /> instead of <p>. I personally don't like using manual line breaks for positioning though.

How to do HTML layout for rows of complex records?

I want to display a list of complex records and trying to simply fit it into a table doesn't seem to be working very well.
I'd like a layout that goes something like this:
Each whole record could be put into a table cell (one cell per row), and then I could use <div> tags within each cell. Is putting divs into table cells likely to cause display problems? It could be simply done with divs anyway, so perhaps that's a bad idea.
Within each record, there are quite a number of components. If I lay these out with divs, what do I need to do to ensure each label/value pair is in the right position? Or is there some better way to ensure a consistent layout?
Obviously the values for each record will be different so to maintain a consistent look I would need the labels to all line up vertically.
If the question seems a bit vague, then it's because my understanding of how to do it is vague... even some help clarifying the question would be great!
Using divs in table cells is fine. Shouldn't cause issues.
Although looking at your mockup, semantically I would say its not a table. No columns, column headings etc.
It looks more like a list of items with more details in them.
I'd use a ul with li's and divs inside to lay things out further.
Also if you need the ID sitting exactly like that you could use a legend element inside each li.
If you are looking at HTML 5, the article tag might fit here. The fact that you have an "Author Element" seems to make it a good fit. If you are not looking at HTML 5 just use a div instead of article. Or as #Moin Zaman mentioned use ul and use li in place of article in my example below.
As for ensuring your labels etc line up vertically this is fairly easy to achieve. Just explicitly set the widths via css.
Here is a quick example:
HTML
<article>
<h2>ID: 123</h2>
<div class="actions">
<input type="button" value="Do Someting" />
<input type="button" value="Do Someting Else" />
<input type="button" value="And Maybe something Else" />
</div>
<div class="description">A fairly long description that takes up basically the entire width of the section, maybe even longer still</div>
<div class="details">
<div class="author"><span>Author:</span>Douglas Adams</div>
<div class="created"><span>Created:</span>1 Jan 2012</div>
<div class="label first"><span>Label 1:</span>Value</div>
<div class="label"><span>Label 2:</span>Value</div>
<div class="label"><span>Label 3:</span>Value</div>
<div style="clear:both; line-height:0px;"> </div><!-- Use A Clear Fix Instead of this, I got Lazy!! -->
</div>
</article>
CSS
article
{
border: solid 2px black;
margin:20px;
padding:5px;
position:relative;
}
article h2
{
background:#FFF;
padding:5px 8px;
position:relative;
top:-15px;
left:5px;
display:inline;
}
article .actions
{
float:right;
width:25%;
text-align:right;
position:relative;
}
article .actions input
{
display:block;
float:right;
clear:both;
}
article .details
{
position:releative;
width:70%;
}
.author
{
float:left;
width:60%;
}
.created
{
float:left;
width:40%
}
.label
{
float:left;
width:30%;
}
.label span, .author span, .created span
{
font-weight:bold;
padding-right:3px;
}
See this fiddle
Note: Use a clear fix instead of having the clearing div.
How about something like this.
HTML
<div>
<span class="label">ID 345</span>
<table>
<tr>
<td class="desc" colspan="3">A fairly long description that takes up basically the entire width of the section, maybe even longer still</td>
<td rowspan="3" class="doStuff">Do Something<br />Do another thing<br />Maybe 1 more thing</td>
</tr>
<tr>
<td class="author"colspan="2"><span>Author: </span>Joe Bloggs</td>
<td class="created"><span>Created: </span>19th June 2012</td>
</tr>
<tr>
<td class="label3"><span>Label 3: </span>Value</td>
<td class="label4"><span>Label 4: </span>Value</td>
<td class="label5"><span>Label 5: </span>Value</td>
</tr>
</table>
</div>​
CSS
div{
margin:17px 10px;
border:2px solid #000;
}
span.label{
background:#FFF;
padding:5px 8px;
position:relative;
top:-10px;
left:5px;
}
table{
width:100%;
}
tr{
background:#ccc;
}
td{
padding:5px 7px;
}
span{
font-weight:bold;
}
jsFiddle - http://jsfiddle.net/QActK/

Setting width of table cell contents based on available width

I have an HTML table whose cells contain, among other things, spans, like this:
...
<td>
<span style="height: 20px; width: 20px; margin-left: 2px;">
<span style="height: 20px; width: 20px; margin-left: 2px;">
<span style="height: 20px; width: 20px; margin-left: 2px;">
</td>
...
I'm looking for a way to shrink the width of those spans, rather than line wrap them, when the containing table cell is too narrow to show them all on one line. I tried playing around with setting the spans' max-width to 20px and then using a percent for the width, but that does not work because the table cell tries to be only as wide as its contents.
The minimum table cell width would be the width needed to display the header on 1 line.
For the visual types, here's what I currently have when there is enough width:
Here's what I currently have when there is not enough width:
And here's what I would like it to look like when there is not enough width for each span to be a full 20px:
In case it's not obvious, the spans are the colored squares in the TXEs, RDBs, and RavenNets columns.
Use <td nowrap> or <td style="white-space:nowrap;"> to avoid the wrapping. A table cell should generally expand to fit its contents, unless it is allowed to wrap, or you have constrained its width in some other way.
Have you considered setting a min-width on the td? or a wrapper div inside the td, but outside the spans?
This sorta kinda seems to do something close to what you want in Firefox 3.6. The crucial requirement seems to be that the table's width cannot be in pixels.
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" type="text/css"
href="http://yui.yahooapis.com/3.0.0/build/cssreset/reset-min.css">
<style type="text/css">
.indicator {
display:block;
white-space:nowrap;
min-width:8px;
max-width:300px;
width:100%;
}
.indicator li {
border:outset 2px;
display:inline-block;
height:80px;
min-width:2px;
max-width:80px;
padding:0 0 0 2px;
width:25%;
}
.ok { background:#0f0 } .caution { background:#ff0 }
.alert { background:#f00 } .inactive { background:#0ff }
table { width:100% }
td { width:100% }
</style>
</head>
<body>
<table><tr><td>
<ul class="indicator">
<li class="ok"></li> <li class="caution"></li>
<li class="alert"></li> <li class="inactive"></li>
</ul>
</td></tr></table>
</body>
</html>
I couldn't find a satisfactory pure-CSS way to do what I wanted. I already had an application config file implemented (like a .ini file, more or less) so I just added my desired width to this config. It's not a general-purpose solution but it fits my requirements just fine.

What HTML/CSS would you use to create a text input with a background?

I have a website design that includes text input fields that look like this:
Input Field http://img401.imageshack.us/img401/4453/picture1ts2.png
I'm wondering what the best solution for creating this input field is.
One idea I have is to always have a div around the input with a background image and all the borders disabled on the input field and specified width in pixels, such as:
<div class="borderedInput"><input type="text" /></div>
I have tried to discourage them from using this format, but they won't be discouraged, so it looks like I'm going to have to do it.
Is this best or is there another way?
--
Trial:
I tried the following:
<style type="text/css">
input.custom {
background-color: #fff;
background:url(/images/input-bkg-w173.gif) no-repeat;
width:173px;
height:28px;
padding:8px 5px 4px 5px;
border:none;
font-size:10px;
}
</style>
<input type="text" class="custom" size="12" />
but in IE (6 & 7) it does the following when you type more than the width:
Over Length http://img240.imageshack.us/img240/1417/picture2kp8.png
I'd do it this way:
<style type="text/css">
div.custom {
background:url(/images/input-bkg-w173.gif) no-repeat;
padding:8px 5px 4px 5px;
}
div.custom input {
background-color: #fff;
border:none;
font-size:10px;
}
</style>
<div class="custom"><input type="text" class="custom" size="12" /></div>
You just have to adjust the padding values so everything fits correctly.
It is - in my eyes- definitely the best solution since in any other case you're working with a whole input field. And the whole input field is - by definition - a box where users can enter text.
If you can rely on JavaScript you could wrap such div-Elements around your input fields programatically.
Edit:
With jQuery you could do it this way:
$( 'input.custom' ).wrap( '<div class="custom"></div>' );
CSS:
div.custom {
background:url(/images/input-bkg-w173.gif) no-repeat;
padding:8px 5px 4px 5px;
}
input.custom {
background-color: #fff;
border:none;
font-size:10px;
}
And your HTML:
<input class="custom" ... />
You don't need the div element, you can assign a background to the input directly.
Edit: Here is the working code. I tested it, but you'll have to adjust it for your needs. As far as I can tell, everything here is needed.
input {
background: #FFF url(test.png) no-repeat bottom right;
width: 120px;
height: 20px;
line-height:20px;
padding:0;
text-indent:3px;
margin:0;
border: none;
overflow:hidden;
}
Edit2: I'm not quite sure why I'm getting downvoted, but this method should work unless you need an image bigger than the input element itself. In that case, you should use the extra div element. However, if the image is the same size as the input, there is no need for the extra markup.
Edit3: Ok, after bobince pointed out a problem, I'm getting a little closer. This will be work in IE6&7 and it's close in FF, but I'm still working on that part.
input {
background: #FFF url(test.png) no-repeat 0px 0px;
background-attachment:fixed;
width: 120px;
height: 20px;
line-height:20px;
padding:0px;
text-indent:3px;
margin:0;
border: none;
}
body>input {
background-position:13px 16px;
}
Edit4: Ok, I think I got it this time, but it requires use of a CSS3 selector, so it won't validate as CSS 2.1.
input {
background: #FFF url(test.png) no-repeat 0px 0px;
background-attachment:fixed;
width: 120px;
height: 20px;
line-height:20px;
padding:0px;
text-indent:3px;
margin:0;
border: none;
}
body>input {
background-position:13px 16px;
}
body>input:enabled {
background-position:9px 10px;
}
body>input will target everything except for IE6, body>input:enabled will target any form elements that aren't disabled for all browsers except for IE 6, 7, & 8. However, because :enabled is a CSS3 selector, it doesn't validate as CSS2.1. I wasn't able to find an appropriate CSS2 selector that would allow me to separate IE7 from the other browsers. If not validating (yet, until the validator switches to CSS3) is a problem for you, then I think your only option is the extra div element.
Have you evaluated using background image like this:
<style type="text/css">
input{
background-color: #AAAAAA;
background-image: url('http://mysite.com/input.gif');
border: 0px;
font-family: verdana;
font-size: 10px;
color: #0000FF;
}
I have done this a few times. I have the background image inside a div and use css to position the input field accordingly.
Have a peek at the following site I created that used this technique and use the code: http://www.ukoffer.com/ (Right hand side Newsletter)
AFAIK, the background scrolling problem can be solved either in Firefox and friends, OR Internet Exploder; but not make everyone happy at once.
I would normally have said to style the input directly, but now that I think of it that div example doesn't sound too bad and should take care of your background image scrolling problem.
In that case you'd set a div as position:relative, and put the input inside it with proper padding and width (or 100% width if padding is 0), background transparent, and put an image on the div.
okoman has gotten the CSS aspect correct. May I suggest using a <label> to improve the semantic structure of the markup?
<label id="for-field-name" for="field-name">
<span class="label-title">Field Name <em class="required">*</em></span>
<input id="field-name" name="field-name" type="text" class="text-input" />
</label>
<style type="text/css">
label, span.label-title { display: block; }
</style>
Not only is this more accessible, but it provides numerous hooks that you can use for any type of DOM manipulation, validation or field-specific styling in the future.
Edit: If you don't want the label title displayed for some reason, you can give it a class of 'accessibility' and set the class to display: none; in the CSS. This will allow screen readers to understand the input but hide it from regular users.
The easiest way to get rid of the overflow without JavaScript is simple:
Create a 3 spans, and set their heights to the height of the
image.
Cut the image into 3 parts, ensuring you cut the image such that
the left and right round parts will be on the 1st and 3rd images
respectively.
Set the background of the 1st span to the image
with the left border, and set it to no-repeat.
Set the background
of the third span to the image with the right border and set it to
no-repeat.
Put the input inside the middle span, remembering to
set its height to the height of the spans, and its background to the
2nd image, and repeat-x only.
That will ensure that the input
will seem to expand horizontally once the input is being filled. No
overlapping, and no JS needed.
HTML
Assuming the image height is 60px, the width of the first and third span is 30px,
<span id="first">nbsp;</span><br />
<span id="second"><input type="text" /></span><br />
<span id="third">nbsp;</span>
CSS
span#first{background:url('firstimage') no-repeat; height:60px; width:30px;}
span#third{background:url('thirdimage') no-repeat; height:60px; width:30px;}
span#second input{background:url('second image') repeat-x; height:60px;}
That should resolve your issue.