Les lignes du tableau doivent être permutées à des positions arbitraires dans le tableau, c'est-à-dire que la ligne i et la ligne j doivent changer de position, où i em> et j ne sont pas nécessairement adjacents. Voir la mise en œuvre actuelle ci-dessous. Les lignes du tableau doivent être triées par colonne; une colonne est spécifiée par sort_index
qui est généré en appuyant sur l'un des en-têtes du tableau.
Le problème avec l'implémentation est que le tableau est trié de manière incrémentielle à chaque clic d'un en-tête, alors qu'il devrait être trié en un seul clic.
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]); } } } }); } });
Il semble que le vrai problème était lié aux fermetures à l'intérieur des boucles. Lorsqu'un en-tête est cliqué, seul le dernier échange de lignes de tableau est réellement mis à jour dans DOM, et en tant que tel, plusieurs clics sont nécessaires pour trier le tableau correctement.
Je publierai ma propre solution à ce sujet, pour clarifier le vrai problème.
3 Réponses :
Je suis d'accord avec M. Polywhirl que faire cela dans le DOM lui-même n'est probablement pas idéal (bien que ce soit tout à fait possible, voir ci-dessous). Le développement Web moderne tend vers l'utilisation d'architectures de style modèle / vue / contrôleur (de différents types) où votre modèle (vos données réelles) est conservé séparément de sa vue (le DOM) et du contrôleur (le navigateur, le DOM et votre code fonctionnant ensemble), qui effectue des actions sur votre modèle (qui sont ensuite reflétées dans la vue). Il existe de nombreux frameworks populaires de style MVC, les plus importants au moment où j'écris ceci (cela change avec le temps) sont React , Vue.js et Angulaire . J'ai inclus un exemple React ci-dessous.
Mais encore une fois, vous pouvez le faire directement sur le DOM. Je vois que vous utilisez jQuery, donc je l'ai utilisé ci-dessous - Voir les commentaires en ligne.
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>
td, th { padding: 4px; } th { cursor: pointer; } table { border-collapse: collapse; } table, td, th { border: 1px solid #ddd; }
// 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") );
Cela peut être un peu plus court, mais je voulais être clair plutôt qu'hyper-concis.
Notez que cela supprime les lignes, les trie et les remet, plutôt que provoquant toutes sortes de modifications DOM sur place.
Voici l'exemple React:
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>
td, th { padding: 4px; } th { cursor: pointer; } table { border-collapse: collapse; } table, td, th { border: 1px solid #ddd; }
// 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); });
Cela utilise diverses fonctionnalités ES2015 +, telles que la déstructuration, les fonctions fléchées et les propriétés abrégées; et utilise également la syntaxe JSX (qui n'est pas une fonctionnalité JavaScript; elle est gérée par Babel dans l'extrait de code). p >
une très belle solution pour moi
C'est très lent ... j'ai écrasé mon chrome ... Je pense que c'est la façon dont vous détachez tout le contenu des TR ... Les solutions ici devraient être utilisées à la place: stackoverflow.com/questions/14267781/...
Vous pouvez soit trier le HTML sur place, soit lier des données à la table et le restituer chaque fois que vous avez besoin de trier.
J'ai utilisé T.J. Le code de Crowder pour le tri sur place, mais l'a transformé en un plugin jQuery et a ajouté une table pouvant être liée. Vous pouvez voir les deux exemples ci-dessous.
<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>
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; }
(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');
Comme indiqué, le problème d'origine était l'échange de lignes. Il semble que le vrai problème soit lié aux fermetures à l'intérieur des boucles. Lorsqu'un en-tête est cliqué, seul le dernier échange de lignes de tableau est réellement mis à jour dans DOM, et en tant que tel, plusieurs clics sont nécessaires pour trier correctement le tableau.
Une solution possible consiste à utiliser la fonction de tri intégrée, comme indiqué ci-dessous.
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); }); } });
Je pense que lier un modèle de données à la table fonctionnerait mieux. De cette façon, vous pouvez modifier les données sous-jacentes, puis restituer la table avec les données.
C'est un peu curieux que vous utilisiez jQuery, mais que vous alliez également directement au DOM pour la plupart des choses que jQuery simplifie pour vous ... Cela peut valoir la peine d'étudier jQuery en profondeur si vous voulez l'utiliser, ou d'étudier le DOM en profondeur si vous ne souhaitez pas utiliser de bibliothèque.