【问题标题】:Aligning decimal points in HTML在 HTML 中对齐小数点
【发布时间】:2010-11-24 16:42:07
【问题描述】:

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

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

解决方案 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 中(尽管它会影响一整列,我想这并不奇怪)。

那么,有人有更好的主意吗?

【问题讨论】:

  • 从理论上讲,你也可以在 CSS 中做到这一点:.dp { text-align: "." }。它是 CSS2.1 中删除的 CSS2.0 属性,但可能会在 CSS3 中恢复。它和align="char" 一样受支持。 IE。一点也不。
  • 对于那些看这个的人,我建议下面的答案组合:打印正确的 DP 数字,使颜色透明并隐藏无意义的数字,确认数字在你的字体中是等宽的重新使用并决定您将接受实际显示的字体不具有该属性的风险(风险很小,因为它只是一个美丽的东西)。这可以在服务器端实现,在 JS 中实现,或者作为两者的组合实现(将所有数字打印到页面中,在 JS 中隐藏无关紧要的数字)。
  • text-align: <string> 存在“风险,如果没有(正确的)实现,可能会在其 CR 期间从规范中删除”(CSS text Level 3):(
  • 这个问题有更现代的解决方案吗? HTML 5 已弃用“col”实体的“align”和“char”属性,而 Javascript 变通方法看起来很……不幸。

标签: html css


【解决方案1】:

请参阅this article by 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>

【讨论】:

  • 有用。他的文章有一个演示,允许您测试 解决方案和 JS 以使其工作。综上所述,JS将单元格内容一一划分为span,测量DP侧,然后在样式表中使用它来分配span宽度。这是一种解决方法,但它确实做了必要的事情,并表明没有合适的方法让浏览器正确地做到这一点。
  • 这是一种不同的、更通用的 JS 方法:github.com/ndp/align-column
【解决方案2】:

另一种格式化数字的方法是:35&lt;span style="visibility: hidden"&gt;.000&lt;/span&gt;。也就是说,用完整的十进制扩展写出来,但用隐形墨水写出尾随小数。这样你就不用担心小数点的宽度了。

【讨论】:

  • visibility: hidden 会更好
  • 我需要前导空格,所以我使用了&lt;span style=\"visibility:hidden\"&gt;00&lt;/span&gt;1234.50。效果很好。
  • 我花了 8 年时间,但我将答案从使用透明度改为可见性。 (本来我没有改,是因为没有在足够多的浏览器中测试。)
【解决方案3】:

令我惊讶的是,在对这个问题的 10 年回答中,没有人提到 Unicode 字符“FIGURE SPACE”(U+2007,&amp;#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>

【讨论】:

  • 我在这里看不到(自动)对齐。你只是把字符放在这里。这就像 Word-Users 使用空格和空白行进行“布局”一样。这不是一个有效的答案。
  • 这与在 linux 终端或其他文本控制台中完成对齐的方式完全相同,其中每个字符都有固定的大小。在大多数字体中,点 (.) 和其他此类标点的大小是可变的,但数字具有相同的大小,因此您可以使用图形空格来填充它们。你可能不喜欢这种技术,但它绝对是有效的。
  • 但这不是自动的。 “linux 终端”自行查找. 所在的位置,并计算需要多少FIGURE SPACE。但在您的示例/解决方案中,用户(或内容创建者)自己需要计算。
【解决方案4】:

作弊;此解决方案的好处:也适用于比例字体。多出一列,将整数部分与小数分隔符和小数分开。然后使用这个 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>

警告:不能保证有效。例如,在 2021 年的 Safari 14 上:

【讨论】:

  • 如果蒂姆·伯纳斯-李死了,他会在他的坟墓里旋转。但是,是的,这是一个答案。
  • 蒂姆·伯纳斯-李为什么会关心这个?这看起来是实现 OP 所追求的一种聪明、稳健且符合标准的方式。
  • @AVProgrammer 因为牺牲了所传达的值(例如这里的数字)的语义凝聚力。小数点左侧和右侧的数字进入单独的 。虽然视觉呈现的输出可能符合预期,但可访问性、连续文本可选择性、 能力和类似的东西被完全忽略了。因此,不能将其称为符合标准,因为标准确实处理的问题不是规定格式良好、视觉可呈现的标记。 <li> <div class="comtext"> <div class="combody"> <span class="comcopy">哦,因为你不能选择它。明白了。</span> </div> </div> </li> <li> <div class="comtext"> <div class="combody"> <span class="comcopy">@AVProgrammer 也是屏幕阅读器。也是错的。</span> </div> </div> </li>
【解决方案5】:

我玩过 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 的可用空间。
【解决方案6】:

您可以打印数字,使它们始终具有相同的小数位数,并右对齐吗?

【讨论】:

  • 实际上,任何体面字体中的数字都是等宽的
  • @Eric 并非所有数字都相同:practicaltypography.com/grids-of-numbers.html
  • 如果您不使用等宽数字,那么按小数点对齐它们的意义何在?小数对齐的重点是使每个数字的列对齐。
【解决方案7】:

数千年前(或 2-3 年)我编写了一个 jQuery 垫片,它模拟 align="char" 似乎仍然有效。它使用 CSS 填充并考虑 colspans,因此它相当聪明,但它确实不是非常漂亮的代码(我当时刚开始使用 javascript)。我希望有人重写它(并承担所有功劳)。

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

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

【讨论】:

  • 链接已失效,这就是为什么理想情况下完整的答案会直接包含在答案中。
  • 对@AlanDeSmet 感到抱歉 - 现已修复。你是对的,但是这个例子有 100 行长,最好的答案可能更好!
【解决方案8】:

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

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

想补充一下;

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

只是为了将 php 加入其中。仍然很大程度上取决于固定宽度的字体,但后者对我有用。由于数据通常已经是表格形式,因此在单元格中添加另一个表格只是输入过多且难以维护。

【讨论】:

    【解决方案9】:

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

    【讨论】:

    • 好点。不像填充那样好的解决方案,但更容易。
    • Em 是行高,而不是字符宽。你可能会很幸运,他们是平等的,或者你可能会很幸运,计算出一个 EM 的一部分,有时恰好与字符宽度几乎完全相同,但一般来说这是一个坏主意。
    • @Charlie:对不起,我的意思是ex,'x' 宽度的 CSS 单位(大概相当于数字的宽度,无论如何在许多字体中都是等宽的)
    • 您可能想要使用ch 而不是exem;见developer.mozilla.org/en-US/docs/Web/CSS/length
    • @Eric:不知道它存在了多久,但根据该页面上的the browser compatability table,它被所有最近的浏览器支持。
    【解决方案10】:

    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();
    });
    

    【讨论】:

      【解决方案11】:

      我已使用 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>";
      }
      

      【讨论】:

        【解决方案12】:

        以前的方法的一个严重问题是只考虑视觉,而没有其他需要或使用表格作为排序过滤,纯数据很重要。

        很遗憾,CSS4 尚不可用。那么一个有效的解决方案可以是在 td 单元格的数据属性中传递值和单位或类型单位。

        <!-- HTML-->
        <table>
          <tbody>
            <tr>
              <td data-value="1876.67542" data-unit="USD"></td>
            </tr>
          </tbody>
        </table>
        

        如果一个单元格有一个数据值,它必须使用 javascript 读取并更新为我们需要的十进制数字。

        // Javascript
        let $td_value = document.querySelectorAll( 'td[data-item]' );
        Array.from( $td_value ).forEach( $r => {
          $r.textContent = parseFloat( $r.getAttribute('data-value') ).toFixed(2);
        });
        

        最后,当我们对数据进行规范化后,它们在使用单色字体和使用 css 选择器放置它们的单元时会看起来很棒。

        /* CSS */
        td[data-value]{
          font-family: monospace;
          text-align: right;
        }
        td[data-unit]::after{
          content: attr(data-unit]);
          font-size: 85%;
          padding-left: .2em;
          opacity: .6;
        }
        

        我放了一个扩展的例子:https://jsfiddle.net/jam65st/wbo63xpu/12/

        【讨论】:

          【解决方案13】:

          丑陋的解决方法,但可以让您免于编写大量代码: 您可以在价格数组(列表)中找到最大数字,然后您可以获取其位数并设置内联样式"width": (maxNumberDigits * 10)px - 这是丑陋的部分! 并且这个数据的容器(如果它的表格是单元格)应该另外有

          display:flex;
          justify-content: flex-end;
          

          结果:

          【讨论】:

            猜你喜欢
            • 2013-04-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-08-29
            相关资源
            最近更新 更多