Как сортировать строки таблицы с использованием Javascript - PullRequest
0 голосов
/ 08 января 2019

Описание

Строки таблицы необходимо поменять местами в произвольных позициях в таблице, то есть строка i и строка j должны изменить позиции, где i и 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 голосов
/ 08 января 2019

Вы можете либо отсортировать HTML-код на месте, либо привязать данные к таблице и перерисовать их всякий раз, когда вам нужно отсортировать.

Я использовал T.J. Код Crowder для сортировки на месте, но превратил его в плагин jQuery и добавил привязываемую таблицу. Вы можете увидеть оба примера ниже.

(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;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
0 голосов
/ 09 января 2019

Описание

Как уже говорилось, исходной проблемой была перестановка строк. Похоже, что настоящая проблема была связана с замыканиями внутри циклов. При щелчке по заголовку в 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);
        });
    }
});
0 голосов
/ 08 января 2019

Я согласен с Мистером Поливайлом , что делать это в самом DOM, вероятно, не идеально (хотя это вполне возможно, см. Ниже). Современная веб-разработка склоняется к использованию архитектур типа модель / представление / контроллер (разных типов), где ваша модель (ваши фактические данные) хранится отдельно от ее представления (DOM) и контроллера (браузера, DOM и ваш код работает вместе), который выполняет действия над вашей моделью (которые затем отражаются в представлении). Существует много популярных фреймворков в стиле MVC, вероятно, наиболее значительными, когда я пишу это (со временем меняется), являются React , Vue.js и Angular, Я включил пример React ниже.

Но, опять же, вы можете сделать это прямо на DOM. Я вижу, что вы используете jQuery, поэтому я использовал его ниже & mdash; Смотрите встроенные комментарии.

// 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 на месте.

Вот пример реакции:

// 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 в фрагменте).

...