如何使用Javascript

时间:2019-01-08 14:47:37

标签: javascript sorting html-table

说明

表行必须在表中的任意位置交换,即,行 i 和行 j 必须更改位置,其中 i 和< em> j 不一定相邻。请参阅下面的当前实现。表行应按列排序; sort_index指定一列,该列是通过按表标题之一生成的。

实施中的问题是,表头是通过单击标题而递增地排序的,而表头应是单击以进行排序的。

var table_index = {
    watched: 0,
    title: 1,
    director: 2,
    year: 3
};

var sort_index = 0;

$(document).ready(function()
{
    var table_headers = document.getElementById("header-item").children;
    for (var k = 0; k < table_headers.length; k++)
    {
        $("#" + table_headers[k].id).bind("click", function(e)
        {
            sort_index = table_index[e.target.id];
            var table = document.getElementById("film-list");
            for (var i = 1; i < table.rows.length - 1; i++)
            {
                var a = table.rows[i].getElementsByTagName("td")[sort_index].innerHTML;
                for (var j = i + 1; j < table.rows.length; j++)
                {
                    var b = table.rows[j].getElementsByTagName("td")[sort_index].innerHTML;
                    var swap = 0;
                    switch (sort_index)
                    {
                    // Alphabetic sort
                    case 0:
                    case 1:
                    case 2:
                        if (b.toLowerCase() < a.toLowerCase())
                            swap = 1;
                        break;
                    // Numeric sort
                    case 3:
                        if (b - a < 0)
                            swap = 1;
                        break;
                    }
                    if (swap == 1)
                    {
                        $(".row-item").eq(i - 1).after(table.rows[j]);
                        $(".row-item").eq(j - 1).after(table.rows[i]);
                    }
                }
            }
        });
    }
});

编辑

看来,真正的问题与循环内的闭包有关。单击标题时,实际上仅在DOM中更新最后一个表行交换,因此需要多次单击才能正确地对表进行排序。

我将为此发布自己的解决方案,以阐明真正的问题。

3 个答案:

答案 0 :(得分:2)

我同意Mr Polywhirl的观点,即在DOM本身中这样做可能并不理想(尽管完全有可能,请参见下文)。现代Web开发倾向于使用模型/视图/控制器风格的架构(各种类型),其中模型(您的实际数据)与模型(DOM)的视图和控制器(浏览器,DOM和您的代码一起运行),对模型执行操作(然后在视图中反映出来)。有许多流行的MVC样式的框架,在我撰写本文时(随着时间的推移而变化),最重要的框架可能是ReactVue.jsAngular。我在下面提供了一个React示例。

但是同样,您可以直接在DOM上执行此操作。我看到您使用的是jQuery,因此在下面使用了它-请参阅内联注释。

// Hook `click` on the table header, but only call our callback if
// that click passes through a `th`
$(".sortable thead").on("click", "th", function() {
    // Which column is this?
    var index = $(this).index();
    
    // Get the tbody
    var tbody = $(this).closest("table").find("tbody");
    
    // Disconnect the rows and get them as an array
    var rows = tbody.children().detach().get();
    
    // Sort it
    rows.sort(function(left, right) {
      // Get the text of the relevant td from left and right
      var $left = $(left).children().eq(index);
      var $right = $(right).children().eq(index);
      return $left.text().localeCompare($right.text());
    });
    
    // Put them back in the tbody
    tbody.append(rows);
});
td, th {
  padding: 4px;
}
th {
  cursor: pointer;
}
table {
  border-collapse: collapse;
}
table, td, th {
  border: 1px solid #ddd;
}
To sort the rows alphabetically by a column's contents, click its header.
<table class="sortable">
  <thead>
    <th>English</th>
    <th>Spanish</th>
    <th>Italian</th>
  </thead>
  <tbody>
    <tr>
      <td>One</td>
      <td>Uno</td>
      <td>Uno</td>
    </tr>
    <tr>
      <td>Two</td>
      <td>Dos</td>
      <td>Due</td>
    </tr>
    <tr>
      <td>Three</td>
      <td>Tres</td>
      <td>Tre</td>
    </tr>
    <tr>
      <td>Four</td>
      <td>Cuatro</td>
      <td>Quattro</td>
    </tr>
  </tbody>
</table>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

它可能会更短一些,但是我想保持清晰,而不是过于简洁。

请注意,这会删除行,对行进行排序并放回去,而不是引起各种就地DOM修改。

这是React示例:

// A React "component" for the table
class MyTable extends React.Component {
    // Initializes the component
    constructor(props) {
        super(props);
        this.state = {
            // Start out unsorted, copying the array (but reusing the entries, as `row`
            // properties -- we do that so we can use their original index as a key)
            sorted: props.data.map((row, index) => ({index, row})),
            sortKey: null
        };
    }
    
    // Sort the view
    sort(by) {
        // Update state...
        this.setState(({sorted, sortKey}) => {
            if (sortKey === by) {
                // ...no update needed, already sorting this way
                return;
            }
            // Copy the array, then sort it (never change state in place)
            sorted = sorted.slice();
            sorted.sort((left, right) => left.row[by].localeCompare(right.row[by]));
            // Return the state updates
            return {sorted, sortKey: by};
        });
    }
    
    // Render the component per current state
    render() {
        const {sorted} = this.state;
        const {headers} = this.props;
        return (
            <table className="sortable">
                <thead>
                    {headers.map(({title, lang}) => <th key={lang} onClick={() => this.sort(lang)}>{title}</th>)}
                </thead>
                <tbody>
                    {sorted.map(({row, index}) =>
                        <tr key={index}>
                        {headers.map(({lang}) => <td key={lang}>{row[lang]}</td>)}
                        </tr>
                    )}
                </tbody>
            </table>
        );
    }
}

// Mount the component
ReactDOM.render(
    <MyTable
        headers={[
            {title: "English", lang: "en"},
            {title: "Spanish", lang: "es"},
            {title: "Italian", lang: "it"}
        ]}
        data={[
            {en: "One", es: "Uno", it: "Uno"},
            {en: "Two", es: "Dos", it: "Due"},
            {en: "Three", es: "Tres", it: "Tre"},
            {en: "Four", es: "Cuatro", it: "Quattro"}
        ]}
    />,
    document.getElementById("root")
);
td, th {
  padding: 4px;
}
th {
  cursor: pointer;
}
table {
  border-collapse: collapse;
}
table, td, th {
  border: 1px solid #ddd;
}
To sort the rows alphabetically by a column's contents, click its header.
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

使用各种ES2015 +功能,例如解构,箭头功能和速记属性;并且还使用JSX语法(这不是JavaScript功能;由代码段中的Babel处理)。

答案 1 :(得分:2)

您可以就地对HTML进行排序,也可以将数据绑定到表并在需要排序时重新呈现它。

我用过T.J. Crowder的代码用于就地排序,但将其变成了jQuery插件并添加了可绑定的表。您可以在下面看到两个示例。

def _validate_file_name(cplex, filename, description):
    """Validate filenames against the set of allowable characters in CPLEX.

    Returns the filename, possibly enclosed in double-quotes, or raises
    a ValueError is unallowable characters are found.

    """
    if filename is None:
        return filename
    matches = _validate_file_name.illegal_characters.search(filename)
    if matches:
        raise ValueError(
            "Unallowed character (%s) found in CPLEX %s file path/name.\n\t"
            "For portability reasons, only [%s] are allowed. Filename: %s"
            % (matches.group(), description,
               _validate_file_name.allowed_characters.replace("\\",''),filename))
    # CPLEX only supports quoting spaces starting in v12.8.
    if ' ' in filename:
        if cplex.version()[:2] >= (12,8):
            filename = '"'+filename+'"'
        else:
            raise ValueError(
                "Space detected in CPLEX %s file path/name\n\t%s\nand "
                "CPLEX older than version 12.8.  Please either upgrade "
                "CPLEX or remove the space from the %s path."
                % (description, filename, description))
    return filename
_validate_file_name.allowed_characters = r"a-zA-Z0-9 \.\-_\%s" % (os.path.sep,)
_validate_file_name.illegal_characters = re.compile(
    '[^%s]' % (_validate_file_name.allowed_characters,))
(function($) {
  $.fn.sortable = function() {
    this.find('thead').on('click', 'th', function(e) {
      var columnIndex = $(this).index();
      var $tbody = $(this).closest('table').find('tbody');
      var rows = $tbody.children().detach().get();
      rows.sort(function(left, right) {
        var $left = $(left).children().eq(columnIndex);
        var $right = $(right).children().eq(columnIndex);
        return $left.text().localeCompare($right.text());
      });
      $tbody.append(rows);
    });
    return this;
  };
  $.fn.renderTable = function(data) {
    var fields = Object.keys(data[0]);
    return this.renderTableHeaders(fields).renderTableRows(fields, data);
  };
  $.fn.renderTableHeaders = function(fields) {
    return this.append($.renderTableHeaders(fields));
  }
  $.fn.renderTableRows = function(fields, data) {
    return this.append($.renderTableRows(fields, data));
  };
  $.tableFromJson = function(data) {
    return $('<table>').renderTable(data);
  };
  $.renderTableHeaders = function(fields) {
    return $('<thead>').append($('<tr>').append(fields
      .map(field => $('<th>').text(field))));
  };
  $.renderTableRows = function(fields, data) {
    return $('<tbody>').append(data
      .map((rec, row) => $('<tr>').append(fields
        .map((field, col) => $('<td>').text(rec[field])))));
  };
  $.bindableTable = function(data, sortable) {
    var $table = $.tableFromJson(data).addClass('bindable');
    if (sortable) {
      $table.dataRef = data;
      $table.addClass('sortable').find('thead').on('click', 'th', function(e) {
        var dataIndex = $(this).text();
        $table.dataRef.sort(function (a, b) {
          var left = new String(a[dataIndex]);
          var right = new String(b[dataIndex]);
          return left.localeCompare(right);
        });
        var fields = Object.keys($table.dataRef[0]);
        $table.find('tbody').replaceWith($.renderTableRows(fields, $table.dataRef));
      });
    }
    return $table;
  };
})(jQuery);

var jsonData = [
  { "id": 1, "name": "John", "age": 24, "make": "Chevrolet", "model": "Silverado", "year": 2016 },
  { "id": 2, "name": "Jack", "age": 36, "make": "Toyota",    "model": "Corolla",   "year": 2018 },
  { "id": 3, "name": "Jill", "age": 29, "make": "Ford",      "model": "Escape",    "year": 2015 }
];

$('body').append($('<h1>').text('HTML sort'));
$.tableFromJson(jsonData).addClass('stylized sortable').sortable().appendTo('body');

$('body').append($('<h1>').text('Databinding sort'));
$.bindableTable(jsonData, true).addClass('stylized').appendTo('body');
body {
  padding: 0.25em !important;
}

h1 {
  font-weight: bold !important;
  margin-top: 0.75em !important;
  margin-bottom: 0.33em !important;
}

table.stylized {
  font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif;
  font-size: 12px;
  text-align: left;
  border-collapse: collapse;
  margin: 4px;
  width: 600px;
}

table.stylized thead th {
  text-transform: capitalize;
  font-size: 13px;
  color: #039;
  background: #b9c9fe;
  padding: 6px;
  cursor: pointer;
}

table.stylized tbody tr:nth-child(odd) {
  background: #f2f5ff;
}

table.stylized tbody tr:nth-child(even) {
  background: #e8edff;
}

table.stylized tbody td {
  border-top: 1px solid #fff;
  color: #669;
  padding: 6px;
}

table.stylized tbody tr:hover td {
  background: #d0dafd;
}

答案 2 :(得分:0)

说明

如上所述,最初的问题是交换行。似乎真正的问题与循环内的闭包有关。单击标题时,实际上仅在DOM中更新最后一个表行交换,因此需要多次单击才能正确地对表进行排序。

解决方案

一种可能的解决方案是使用内置的排序功能,如下所示。

var table_index = {
    watched: 0,
    title: 1,
    director: 2,
    year: 3
};

var sort_index = 0;

var tbody = $("tbody").children().get();

$(document).ready(function()
{
    var table_headers = $("thead").children();
    for (var k = 0; k < table_headers.length; k++)
    {
        $("#" + table_headers[k].id).bind("click", function(e)
        {
            sort_index = table_index[e.target.id];
            switch (sort_index)
            {
            // Alphabetic sort
            case 0:
            case 1:
            case 2:
                tbody.sort(function(a, b) {
                    var l = $(a).children().eq(sort_index).text();
                    var r = $(b).children().eq(sort_index).text();
                    if (r.toLowerCase() < l.toLowerCase())
                        return 1;
                    else if (r.toLowerCase() > l.toLowerCase())
                        return -1;
                    else
                        return 0;
                });
                break;
            // Numeric sort
            case 3:
                tbody.sort(function(a, b) {
                    var l = $(a).children().eq(sort_index).text();
                    var r = $(b).children().eq(sort_index).text();
                    return l - r;
                });
                break;
            }
            $("tbody").children().detach();
            $("tbody").append(tbody);
        });
    }
});