17

rowspan 属性を持つセルを含むテーブルがあります。次のことを希望します。

  1. a が非表示になるたびtrに、テーブルは正しく再配置されます
  2. a が再度表示されるたびに、tr元の状態に復元されます

したがって、このようなテーブルがある場合、クリックしXてもレイアウトが破壊されることはありません。ボタンをクリックするとcome back、元のレイアウトが復元されます。

(ボトムアップからすべての行を削除してみてください。右から左に復元するよりも、これは望ましいフローです)

私はいくつかの半解決策を持っていましたが、すべてが複雑すぎるようです.これを処理する良い方法があると確信しています.

4

3 に答える 3

23

OK、私はこの質問に本当に長い時間を費やしたので、ここに行きます...

実際のソリューションを見たいだけの方は、ここをクリックしてください

更新:視覚的な列の計算方法を変更して、テーブルを反復処理し、2 次元配列を作成しました。jQuery メソッドを使用した古い方法を確認するにはoffset()ここをクリックしてください。コードは短くなりますが、時間がかかります。

行を非表示にするときに、すべてのセルを非表示にしたい一方で、疑似セル(つまり、セルrowspan属性のために次の行にあるように見えるセル) を保持したいため、問題が発生します。これを回避するために、 で非表示のセルに出くわすたびにrowspan、次の表示されている行に移動しようとします (rowspan進むにつれて値が減少します)。元のセルまたはそのクローンのいずれかを使用して、疑似セルを含むすべての行に対してもう一度テーブルを反復し、行が非表示の場合はrowspan再度デクリメントします。(理由を理解するには、実際の例を見てください。青い行が非表示になっている場合、赤いセル 9 の行スパンを 2 から 1 に減らす必要があることに注意してください。そうしないと、緑の 9 が右に押し出されます)。

それを念頭に置いて、行が表示/非表示になるたびに、次の関数を適用する必要があります。

function calculate_rowspans() {
  // Remove all temporary cells
  $(".tmp").remove();

  // We don't care about the last row
  // If it's hidden, it's cells can't go anywhere else
  $("tr").not(":last").each(function() {
    var $tr = $(this);

    // Iterate over all non-tmp cells with a rowspan    
    $("td[rowspan]:not(.tmp)", $tr).each(function() {
      $td = $(this);
      var $rows_down = $tr;
      var new_rowspan = 1;

      // If the cell is visible then we don't need to create a copy
      if($td.is(":visible")) {
        // Traverse down the table given the rowspan
        for(var i = 0; i < $td.data("rowspan") - 1; i ++) {

          $rows_down = $rows_down.next();
          // If our cell's row is visible then it can have a rowspan
          if($rows_down.is(":visible")) {
            new_rowspan ++;
          }
        }
        // Set our rowspan value
        $td.attr("rowspan", new_rowspan);   
      }
      else {
        // We'll normally create a copy, unless all of the rows
        // that the cell would cover are hidden
        var $copy = false;
        // Iterate down over all rows the cell would normally cover
        for(var i = 0; i < $td.data("rowspan") - 1; i ++) {
          $rows_down = $rows_down.next();
          // We only consider visible rows
          if($rows_down.is(":visible")) {
            // If first visible row, create a copy

            if(!$copy) {
              $copy = $td.clone(true).addClass("tmp");
              // You could do this 1000 better ways, using classes e.g
              $copy.css({
                "background-color": $td.parent().css("background-color")
              });
              // Insert the copy where the original would normally be
              // by positioning it relative to it's columns data value 
              var $before = $("td", $rows_down).filter(function() {
                return $(this).data("column") > $copy.data("column");
              });
              if($before.length) $before.eq(0).before($copy);
              else $(".delete-cell", $rows_down).before($copy);
            }
            // For all other visible rows, increment the rowspan
            else new_rowspan ++;
          }
        }
        // If we made a copy then set the rowspan value
        if(copy) copy.attr("rowspan", new_rowspan);
      }
    });
  });
}

次の非常に難しい問題は、行内のセルのコピーを配置するインデックスを計算することです。この例では、青色のセル 2 は行内の実際のインデックスが 0 であることに注意してください。つまり、行内の最初の実際のセルですが、列 2 (0-indexed) にあることが視覚的にわかります。

ドキュメントが読み込まれるとすぐに、これを一度だけ計算するというアプローチを取りました。次に、この値をセルのデータ属性として保存し、そのコピーを適切な場所に配置できるようにします (これについて多くのひらめきの瞬間があり、何ページにもわたるメモを作成しました!)。この計算を行うためにmatrix、すべての使用済みビジュアル列を追跡する 2 次元配列を構築することになりました。同時に、セルの元のrowspan値を保存します。これは、行の非表示/表示によって変化するためです。

function get_cell_data() {
    var matrix = [];  
    $("tr").each(function(i) {
        var $cells_in_row = $("td", this);
        // If doesn't exist, create array for row
        if(!matrix[i]) matrix[i] = [];
        $cells_in_row.each(function(j) {
            // CALCULATE VISUAL COLUMN
            // Store progress in matrix
            var column = next_column(matrix[i]);
            // Store it in data to use later
            $(this).data("column", column);
            // Consume this space
            matrix[i][column] = "x";
            // If the cell has a rowspan, consume space across
            // Other rows by iterating down
            if($(this).attr("rowspan")) {
                // Store rowspan in data, so it's not lost
                var rowspan = parseInt($(this).attr("rowspan"));
                $(this).data("rowspan", rowspan);
                for(var x = 1; x < rowspan; x++) {
                    // If this row doesn't yet exist, create it
                    if(!matrix[i+x]) matrix[i+x] = [];
                    matrix[i+x][column] = "x";
                }
            }
        });
    });

    // Calculate the next empty column in our array
    // Note that our array will be sparse at times, and
    // so we need to fill the first empty index or push to end
    function next_column(ar) {
        for(var next = 0; next < ar.length; next ++) {
            if(!ar[next]) return next;
        }
        return next;
    }
}

次に、ページの読み込み時にこれを適用するだけです:

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

(注: ここのコードは私の jQuery.offset()の代替コードよりも長いですが、おそらく計算の方が速いでしょう。間違っていたら訂正してください)。

于 2013-07-19T13:42:57.350 に答える
0

行を非表示にするときに行を上にシフトしたいが、セルを左にシフトしたくないと仮定しています。

これが私が得たものですhttp://codepen.io/anon/pen/prDcK

2 つの CSS ルールを追加しました。

#come_back_container{height: 30px;}
td[rowspan='0']{background-color: white;}

使用したhtmlは次のとおりです。

<div id="come_back_container"></div>
<table id="dynamic_table" cellpadding=7></table>
<table id="dynamic_table2" cellpadding=7>
  <tr style="background-color: red">
    <td rowspan="5">a</td>
    <td rowspan="1">b</td>
    <td rowspan="5">c</td>
    <td rowspan="1">d</td>
    <td rowspan="2">e</td>
  </tr>
  <tr style="background-color: grey">
    <td rowspan="0">f</td>
    <td rowspan="1">g</td>
    <td rowspan="0">h</td>
    <td rowspan="1">i</td>
    <td rowspan="0">j</td>
  </tr>
  <tr style="background-color: blue">
    <td rowspan="0">k</td>
    <td rowspan="1">l</td>
    <td rowspan="0">m</td>
    <td rowspan="1">n</td>
    <td rowspan="1">o</td>
  </tr>
  <tr style="background-color: yellow">
    <td rowspan="0">p</td>
    <td rowspan="1">q</td>
    <td rowspan="0">r</td>
    <td rowspan="1">s</td>
    <td rowspan="2">t</td>
  </tr>
  <tr style="background-color: green">
    <td rowspan="0">u</td>
    <td rowspan="1">v</td>
    <td rowspan="0">w</td>
    <td rowspan="1">x</td>
    <td rowspan="0">y</td>
  </tr>
</table>

最初のルールは、テーブルの上端を同じ場所に保つことです。2 番目のルールは、背景に溶け込んでセルが空白に見えるようにするため、それに応じて変更します。

最後に、js は次のとおりです。

$(function () {
  //firstTable()

  var myTb2 = new dynamicTable();
  myTb2.createFromElement( $("#dynamic_table2") );
  myTb2.drawTable()

  $(window).on("tr_hide", function (e,data){
    var tbl = data.ctx,
        rowIndex = data.idx;
    tbl.hideRow.call(tbl, rowIndex);
  })
  $(window).on("tr_show", function (e,data){
    var tbl = data.ctx,
        rowIndex = data.idx;
    tbl.showRow.call(tbl, rowIndex);
  })
})

function dynamicTableItem(){
  this.height = null;
  this.content = null;
}

function dynamicTableRow(){
  this.color = null;
  this.items = []
  this.show = true

  this.setNumColumns = function(numCols){
    for(var i=0;i<numCols;i++){
      var item = new dynamicTableItem(); 
      item.height = 0;
      this.items.push(item)
    }
  }

  this.addItem = function(index, height, content){
    var item = new dynamicTableItem();
    item.height = height;
    item.content = content;
    if(index>=this.items.length){ console.error("index out of range",index); }
    this.items[index] = item;
  }
}

function dynamicTable(){
  this.element = null;
  this.numCols = null;
  this.rows = []

  this.addRow = function(color){
    var row = new dynamicTableRow();
    row.color = color;
    row.setNumColumns(this.numCols)
    var length = this.rows.push( row )
    return this.rows[length-1]
  }
  this.drawTable = function(){
    this.element.empty()

    var cols = [],
        rowElements = [];
    for(var i=0;i<this.numCols;i++){
      cols.push( [] )
    }

    for(var r=0; r<this.rows.length; r++){
      var row = this.rows[r]
      if(row.show){
        var $tr = $("<tr>"),
            delete_cell = $("<td>"),
            delete_btn = $("<button>").text("x")
        var data = {ctx: this, idx: r};
        delete_btn.on("click", data, function(e){
          $(window).trigger("tr_hide", e.data);
        })
        delete_cell.addClass("deleteCell");
        $tr.css( {"background": row.color} );

        delete_cell.append(delete_btn);
        $tr.append(delete_cell);
        this.element.append($tr);
        rowElements.push( $tr );

        for(var i=0; i<row.items.length; i++){
          cols[i].push( row.items[i] );
        }
      }
    }

    for(var c=0; c<cols.length; c++){
      var cellsFilled = 0;
      for(var r=0; r<cols[c].length; r++){
        var item = cols[c][r]
        var size = item.height;
        if(r>=cellsFilled){
          cellsFilled += (size>0 ? size : 1);
          var el = $("<td>").attr("rowspan",size);
          el.append(item.content);
          rowElements[r].children().last().before(el);
        }
      }
    }
  }

  this.hideRow = function(rowIndex){
    var row = this.rows[rowIndex]
    row.show = false; 

    var come_back_btn = $("<button>").text("come back");
    come_back_btn.css( {"background": row.color} );
    var data = {ctx:this, idx:rowIndex};
    come_back_btn.on("click", data, function(e){
      $(window).trigger("tr_show", e.data);
      $(this).remove();
    });
    $("#come_back_container").append(come_back_btn);

    this.drawTable();
  }

  this.showRow = function(rowIndex){
    this.rows[rowIndex].show = true;
    this.drawTable();
  }

  this.createFromElement = function(tbl){
    this.element = tbl;
    var tblBody = tbl.children().filter("tbody")
    var rows = tblBody.children().filter("tr")
    this.numCols = rows.length

    for(var r=0;r<rows.length;r++){
      var row = this.addRow( $(rows[r]).css("background-color") );
      var items = $(rows[r]).children().filter("td");

      for(var i=0;i<items.length;i++){
        var item = $(items[i]);
        var height = parseInt(item.attr("rowspan"));
        var contents = item.contents();
        row.addItem(i,height,contents);
      }
    }
    //console.log(this); 
  }

}

function firstTable(){
  var myTable = new dynamicTable();
  myTable.element = $("#dynamic_table");
  myTable.numCols = 5

  var red = myTable.addRow("red"); 
  red.addItem(0,5);
  red.addItem(1,1);
  red.addItem(2,5);
  red.addItem(3,1);
  red.addItem(4,2);

  var white = myTable.addRow("grey");
  //white.addItem(0,0);
  white.addItem(1,1);
  //white.addItem(2,0);
  white.addItem(3,1);
  //white.addItem(4,0);

  var blue = myTable.addRow("blue");
  //blue.addItem(0,3);  //try uncommenting this and removing red
  blue.addItem(1,1);
  //blue.addItem(2,0);
  blue.addItem(3,1);
  blue.addItem(4,1);

  var yellow = myTable.addRow("yellow");
  //yellow.addItem(0,0);
  yellow.addItem(1,1);
  //yellow.addItem(2,0);
  yellow.addItem(3,1);
  yellow.addItem(4,2);

  var green = myTable.addRow("green");
  //green.addItem(0,0);
  green.addItem(1,1);
  //green.addItem(2,0);
  green.addItem(3,1);
  //green.addItem(4,0);

  myTable.drawTable();
}

明確な変数名とメソッド名を使用しようとしましたが、クエストがある場合は質問してください。

PS-現在、セルにコンテンツを追加する簡単な方法がないことはわかっていますが、行が消えることだけを要求しました。

于 2013-08-01T22:24:22.463 に答える