在HTML中对齐小数点


71

我有一张表格,其中一列包含十进制数字。我希望以类似于文字处理器的“十进制制表符”功能的方式对齐它们,以便所有点都位于一条垂直线上。

目前,我有两种可能的解决方案,但我希望有更好的解决方案...

解决方案1:在HTML中拆分数字,例如

<td><div>1234</div><div class='dp'>.5</div></td>

.dp { width: 3em; }

(是的,此解决方案不能完全按原样工作。但是,此概念是有效的。)

解决方案2:我发现提到

<col align="char" char=".">

根据参考页,这是HTML4的一部分,但是在我必须使用的浏览器FF3.5,Safari 4或IE7中却无法使用。它还存在无法将数字格式提取到CSS的问题(尽管由于它影响到整个列,所以我认为这并不奇怪)。

因此,有谁有更好的主意?


8
从理论上讲,您也可以在CSS:中进行此操作.dp { text-align: "." }。这是CSS2.0属性,已在CSS2.1中删除,但可能会在CSS3中重新出现。虽然也支持它align="char"。即一点也不。
墨卡托

1
对于那些关注此问题的人,我建议使用以下答案的组合:打印正确数量的DP,使颜色透明并隐藏无关紧要的数字,在确认所用字体中的数字已等距并决定接受之后实际显示的字体将不具有该属性的风险(风险很小,因为这只是一件漂亮的事情)。可以在JS中在服务器端实现,也可以将两者结合在一起(将所有数字打印到页面中,在JS中隐藏无关紧要的数字)。
ijw


2
text-align:<string> “处于危险之中,如果没有(正确的)实现,可能会在其CR期间从规范中删除”(CSS文本级别3):(
sam 2012年

2
对于这个问题是否有更现代的解决方案?HTML 5已弃用了“ col”实体的“ align”和“ char”属性,而Javascript的解决方法似乎是如此……很不幸。
MikeB 2013年

Answers:


21

请参阅Krijn Hoetmer的这篇文章,了解您的选择以及如何实现这一点。该解决方案的实质是使用CSS和JS实现此目的:

(function() {
  var currencies = /(\$|€|&euro;)/;
  var leftWidth = 0, rightWidth = 0;
  for(var tableCounter = 0, tables = document.getElementsByTagName("table");
      tableCounter < tables.length; tableCounter++) {
    if(tables[tableCounter].className.indexOf("fix-align-char") != -1) {
      var fCols = [], leftPart, rightPart, parts;
      for(var i = 0, cols = tables[tableCounter].getElementsByTagName("col"); i < cols.length; i++) {
        if(cols[i].getAttribute("char")) {
          fCols[i] = cols[i].getAttribute("char");
        }
      }
      for(var i = 0, trs = tables[tableCounter].rows; i < trs.length; i++) {
        for(var j = 0, tds = trs[i].getElementsByTagName("td"); j < tds.length; j++) {
          if(fCols[j]) {
            if(tds[j].innerHTML.indexOf(fCols[j]) != -1) {
              parts = tds[j].innerHTML.split(fCols[j]);
              leftPart = parts.slice(0, parts.length -1).join(fCols[j]);
              leftPart = leftPart.replace(currencies, "<span class='currency'>$1</span>");
              rightPart = fCols[j] + parts.pop();
              tds[j].innerHTML = "<span class='left'>" + leftPart + "</span><span class='right'>" + rightPart + "</span>";
            } else {
              tds[j].innerHTML = tds[j].innerHTML.replace(currencies, "<span class='currency'>$1</span>");
              tds[j].innerHTML = "<span class='left'>" + tds[j].innerHTML + "</span>";
            }
            tds[j].className = "char-align";
            var txt = document.createTextNode(tds[j].firstChild.offsetWidth);
            if(leftWidth < tds[j].firstChild.offsetWidth) {
              leftWidth = tds[j].firstChild.offsetWidth;
            }
            if(tds[j].childNodes[1]) {
              txt = document.createTextNode(tds[j].childNodes[1].offsetWidth);
              if(rightWidth < tds[j].childNodes[1].offsetWidth) {
                rightWidth = tds[j].childNodes[1].offsetWidth;
              }
            }
          }
        }
      }
    }
  }
  // This is ugly and should be improved (amongst other parts of the code ;)
  var styleText = "\n" +
      "<style type='text/css'>\n" +
      "  .fix-align-char td.char-align { width: " + (leftWidth + rightWidth) + "px; }\n" +
      "  .fix-align-char span.left { float: left; text-align: right; width: " + leftWidth + "px; }\n" +
      "  .fix-align-char span.currency { text-align: left; float: left; }\n" +
      "  .fix-align-char span.right { float: right; text-align: left; width: " + rightWidth + "px; }\n" +
      "</style>\n";
  document.body.innerHTML += styleText;
})();
table {
  border-collapse: collapse;
  width: 600px;
}
th {
  padding: .5em;
  background: #eee;
  text-align: left;
}
td {
  padding: .5em;
}
#only-css td.char-align {
  width: 7em;
}
#only-css span.left {
  float: left;
  width: 4em;
  text-align: right;
}
#only-css span.currency {
  float: left;
  width: 2em;
  text-align: left;
}
#only-css span.right {
  float: right;
  width: 3em;
  text-align: left;
}
<table id="only-css">
  <thead>
    <tr>
      <th>Number</th>
      <th>Description</th>
      <th>Costs</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Lorem ipsum dolor sit amet</td>
      <td class="char-align">
        <span class="left">
          <span class="currency">$</span>3
        </span>
        <span class="right">,99</span>
      </td>
    </tr>
    <tr>
      <td>2</td>
      <td>Consectetuer adipiscing elit</td>
      <td class="char-align">
        <span class="left">
          <span class="currency">$</span>13
        </span>
        <span class="right">,95</span>
      </td>
    </tr>
    <tr>
      <td>3</td>
      <td>Pellentesque fringilla nisl ac mi</td>
      <td class="char-align">
        <span class="left">
          <span class="currency">$</span>4
        </span>
        <span class="right"></span>
      </td>
    </tr>
    <tr>
      <td>4</td>
      <td>Aenean egestas gravida magna</td>
      <td class="char-align">
        <span class="left">
          <span class="currency">$</span>123
        </span>
        <span class="right">,999</span>
      </td>
    </tr>
  </tbody>
</table>


3
有用。他的文章中有一个演示,允许您测试<COL>解决方案和JS使其生效。总之,JS将单元格内容一一划分为跨度,测量DP边,然后在样式表中使用它来分配跨度宽度。这是一种变通办法,但确实做到了必要,并暗示没有让浏览器正确执行的体面方法。
ijw

1
这是另一种更通用的JS方法:github.com/ndp/align-column
ndp

15

格式化数字的另一种方法是这样的: 35<span style="visibility: hidden">.000</span>。也就是说,以完整的十进制扩展名写出,但在不可见的墨水中写出尾随的十进制。这样,您不必担心小数点的宽度。


7
visibility: hidden会更好
Eric 2010年

我需要前导空格,所以我用<span style=\"visibility:hidden\">00</span>1234.50。效果很好。
安德鲁·飞利浦

9
我花了8年的时间,但我将答案从使用透明度更改为可见性。(本来我没有更改它,因为我没有在足够多的浏览器中进行测试。)
David Leppik

11

作弊; 该解决方案的优点:也适用于比例字体。再增加一列,然后从小数点分隔符和小数点分隔整数部分。然后使用此CSS并在标题行中合并两列:

table {border-collapse:collapse;}
td {padding:0px;margin:0px;border:0px;}
td+td {text-align:right;}
td, td+td+td {text-align:left;}
<table>
    <tr><th>Name</th><th colspan=2>Height</th></tr>
    <tr><td>eiffeltower</td> <td>324</td> <td></td></tr>
    <tr><td>giraffe</td> <td>5</td> <td>,30</td></tr>
    <tr><td>deer</td> <td>1</td> <td></td></tr>
    <tr><td>mouse</td> <td>0</td> <td>,03</td></tr>
</table>


26
如果蒂姆·伯纳斯·李死了,他将在坟墓中旋转。但是,是的,这是一个答案。
2011年

3
蒂姆·伯纳斯·李为什么会对此表示关注?这看起来是实现OP所追求的一种聪明,强大,符合标准的方式。
AVProgrammer

16
@AVProgrammer,因为牺牲了传达的值(例如,此处的数字)的语义内聚性。小数点标记左侧和右侧的数字分别进入<td>。尽管视觉呈现的输出可能符合期望,但可访问性,连续的文本选择能力,<title>能力和类似的东西都被完全忽略了。因此,我们不能称其为符合标准的,因为标准除了规定了格式正确的,可视觉呈现的标记之外,还处理其他问题。
罗伯特·蒙菲拉,2016年

哦,因为您无法选择它。得到它了。
AVProgrammer

@AVProgrammer也是屏幕阅读器。同样,这是错误的。
伊恩·博伊德

8

我玩了jQuery并提出了这个...

$(document).ready(function() {
  $('.aBDP').each(function() {
    var wholePart, fractionPart;
    wholePart = Math.floor($(this).text()-0);
    fractionPart = Math.floor(($(this).text() % 1)*10000 + 0.5) / 10000 + "";
    html  = '<span class="left">' + wholePart + '.' + '</span>';
    html += '<span class="right">' + fractionPart.substring(2) + '</span>';
    $(this).html(html); 
  })
})
.right {
    text-align: left;
}
.left {
    float:left;
    text-align: right;
    width:10em;
}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js" type="text/javascript"></script>

<table width="600" border="1">  
  <tr><th></th><th>Aligned Column</th></tr>
  <tr><th>1st Row</th><td class='aBDP'>1.1</td></tr>
  <tr><th>2nd Row</th><td class='aBDP'>10.01</td></tr>  
  <tr><th>3rd Row</th><td class='aBDP'>100.001</td></tr>  
  <tr><th>4th Row</th><td class='aBDP'>1000.0001</td></tr>
</table>

它似乎有效。


不错,尽管您需要确保您的列首先具有超过10em的可用空间。
ijw


4

我很惊讶在回答这个问题的10年中,没有人提到Unicode字符“ FIGURE SPACE”(U + 2007,&#8199;

这是一个空白字符,设计为(如果字体作者遵循标准,则由字体作者设计)为与数字相同的宽度并保持其间距,就像其更著名的表亲No-Break Space。您可以使用它在左侧或右侧将数字填充为特定的字符串大小,并注意将列或div对齐在同一侧。

示例,左对齐和左填充图形空间:

<p style="font-family: sans-serif">
  10000 <br>
  &#8199;&#8199;123.4 <br>
  &#8199;&#8199;&#8199;&#8199;3.141592
</p>

<p style="font-family: serif">
  10000 <br>
  &#8199;&#8199;123.4 <br>
  &#8199;&#8199;&#8199;&#8199;3.141592
</p>


2

几千年前(或2-3年),我写了一个jQuery shim,它模仿align =“ char”似乎仍然有效。它使用CSS填充并考虑了colspans,因此它比较聪明,但是它的代码确实不是很漂亮(那时候我才刚开始使用javascript)。我希望有人重写它(并承担全部功劳)。

同时,请查看这是否对您有帮助:https : //gist.github.com/inanimatt/f27ffd25c174e9d8a0907455395d147d

琐事:浏览器不正确支持列样式的原因是表是2D数据结构,而DOM(Javascript和CSS对其进行操作以及HTML5的定义方式)纯粹是分层的,因此不能表示两个列和行。相反,它仅定义行和单元格,根本不表示列。


链接已死,这就是为什么理想情况下将完整答案直接包含在答案中的原因。
艾伦·德·史密特

抱歉@AlanDeSmet-现在已修复。您是对的,但示例的长度为100行,最上面的答案可能更好!
inanimatt

1

我喜欢简短的答案,尽管长的答案也很重要。

35<span style="color:transparent">.000</span>

并想添加;

<TD><div style='float:right;'><?php echo number_format($totalAmount,2); ?></div></TD>

只是把php混在一起。仍然很大程度上取决于固定宽度的字体,但是后者对我有用。由于数据已经是表格形式的,因此在单元格中添加另一个表的类型太多,难以维护。


0

如果数字是等距的,则可以使用javascript来调整单元格上的填充(以ems为单位),具体取决于小数点前的位数。否则,可能会很棘手。


好点子。解决方案不像padding那样好,但是更容易。
埃里克

Em是行高,而不是字符宽度。您可能会很幸运,并且它们是相等的,或者您可能会很幸运,并且算出了EM的一部分,有时它恰好与字符宽度几乎完全相同,但是总的来说这是一个坏主意。
查理

@Charlie:对不起,我的意思ex是CSS单位表示一个'x'的宽度(大概等于数字的宽度,无论如何,它们在许多字体中都是等距的)
Eric 2010年

1
您可能要使用ch代替exem; 参见developer.mozilla.org/zh-CN/docs/Web/CSS/length
codermonkeyfuel 2016年

@Eric:不知道它存在了多久,但是根据该页面上的浏览器兼容性表,所有最近的浏览器都支持它。
codermonkeyfuel

0

Krijn Hoetmer的功能干扰了prettyPhoto(http://www.no-margin-for-errors.com/projects/prettyphoto-jquery-lightbox-clone/),所以我制作了一个jQuery版本。货币部分已删除,因为应使其动态化,而不是根据预定义货币替换字符串。

需要的是phpjs中的空函数:http ://phpjs.org/functions/empty:392 。

使用的jQuery是1.6版。

/* This function will align table columns on the char if in the col from the 
 * colgroup has the property 'align="char"' and a attribute 'char'. The alignment
 * is done on the first occurence of the specified char.
 * 
 * The function is inspired from:
 * 
 * http://krijnhoetmer.nl/stuff/javascript/table-align-char/
 * http://stackoverflow.com/questions/1363239/aligning-decimal-points-in-html
 */
function alignNumbers()
{
  var table; /* This will store the table currently working on . */
  var i = 0; /* Every column can have it's own width, the counter makes the class name unique. */

  /* Get all tables for which the alignment fix must be done. 
   *
   * Note: this could even be further optimized by just looking for tables where
   * there is a a col with 'align="char"'. 
   */
  $('table.fix-align-char').each(function(index)
  {
    table = $(this);

    /* All table columns are fetched to have a correct index, without it it's
     * hard to get the correct table cells.
     */
    $(this).find('col').each(function(index)
    {
      /* Only those table cells are changed for which the alignment is set to
       * char and a char is given.
       */
      if ($(this).prop('align') == 'char' && !empty($(this).attr('char')))
      {
        /* Variables for storing the width for the left and right part (in pixels). */
        var left_width = 0, right_width = 0;
        var col, left_part, right_part, parts, new_html;
        i++; /* Increase the counter since we are working on a new column. */
        col = $(this);

        /* For the col index + 1 (nth-child starts counting at 1), find the table
         * cells in the current table.
         */
        table.find('> tbody > tr > td:nth-child('+ (index + 1) +')').each(function(index)
        {
          /* Split the html on the specified char. */
          parts = $(this).html().split(col.attr('char'));
          new_html = '';


          /* The first element is always the left part. The remaining part(s) are
           * the right part. Should there be more chars in the string, the right
           * parts are rejoined again with the specified char. 
           */
          left_part = parts.shift();
          right_part = parts.join(',');

          /* Add a left part to the new html if the left part isn't empty*/
          if (!empty(left_part))
          {
            new_html = new_html + '<span class="left">' + left_part + '</span>';
          }

          /* Add the specified char and the right part to the new html if 
           * the right part isn't empty*/
          if (!empty(right_part))
          {
            new_html = new_html + col.attr('char') + '<span class="right">' + right_part + '</span>';
          }

          /* If there is a new html, the width must be determined and a class is
           * added.
           * 
           * Note: outerWidth is used instead of width so padding, margin and
           * borders are taken into account.
           */
          if (!empty(new_html))
          {
            $(this).html(new_html); /* Set the new html. */
            $(this).addClass('char-align-' + i); /* Add a class to the table cell. */

            /* Get the left span to determine its outer width. */
            leftSpan = $(this).children('.left');

            if (!empty(leftSpan) && left_width < leftSpan.outerWidth())
            {
              left_width = leftSpan.outerWidth();
            }

            /* Get the right span to determine its outer width. */
            rightSpan = $(this).children('.right');

            if (!empty(rightSpan) && right_width < rightSpan.outerWidth())
            {
              right_width = rightSpan.outerWidth();
            }

          }

        });

        /* Only if any width is larger then 0, add a style. */
        if (left_width > 0 || right_width > 0)
        {
          style_text = '<style type="text/css">.fix-align-char td.char-align-' + (i) + ' span.left { float: left; text-align: right; width: ' + (left_width) + 'px; }\n.fix-align-char td.char-align-' + (i) + ' span.right { float: right; text-align: left; width: ' + right_width + 'px; }</style>';
          $('head').append(style_text);
        }

      }
    });
  });

}

$(document).ready(function(){
  alignNumbers();
});

0

我已经使用JavaScript来解决此问题...这是我的HTML。

<body>
<table id="nadis">

</tr>
</table>

</body>

这是我的JavaScript。

var numarray = ["1.1", "12.20", "151.12", 1000.23,12451];
var highetlen = 0;
for(var i=0; i<numarray.length; i++){
    var n = numarray[i].toString();
  var res= n.split(".");
  n = res[0];

  if(highetlen < n.length){
    highetlen = n.length;
  }

}

for(var j=0; j<numarray.length; j++){
    var s = numarray[j].toString();
  var res= s.split(".");
  s = res[0];

    if(highetlen > s.length){
  var finallevel = highetlen - s.length;

  var finalhigh = "";
  for(k=0;k<finallevel;k++){
  finalhigh = finalhigh+ '&#160; ';
  }
    numarray[j] = finalhigh + numarray[j];
  }
  var nadiss = document.getElementById("nadis");
  nadiss.innerHTML += "<tr><td>" + numarray[j] + "</td></tr>";
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.