JavaScript: ordinare gli elementi usando gli attributi di dati

In questo tutorial vedremo come implementare l'ordinamento degli elementi con gli attributi di dati in JavaScript.

Dobbiamo pensare in termini di tipi di dati contenuti negli attributi. I tipi di dati più comuni sono quelli di tipo numerico, ossia interi, decimali e date. A ben vedere anche le date possono essere considerate come numeri interi se le convertiamo in timestamp Unix.

JavaScript dispone del metodo sort() degli array, che può essere utilizzato sulle NodeList del DOM se le convertiamo in array con il metodo Array.from(). Quindi dobbiamo interrogare l'oggetto dataset di ciascun elemento accedendo alla proprietà che vogliamo valutare.

A questo punto avremo un nuovo array di elementi del DOM ordinato secondo i nostri criteri. Non dobbiamo far altro che aggiornare il DOM eseguendo un loop sugli elementi e invocando il metodo appendChild() del genitore di tali elementi.

Resta il problema di aggiungere automaticamente gli attributi di dati agli elementi, specificando il nome dell'attributo ed il suo valore. Possiamo specificare una matrice bidimensionale in cui andremo a specificare il tipo di dati e il nome della chiave. Quindi effettuando un loop sugli elementi che compongono le righe estrarremo il valore che ci interessa dal nodo testuale di ciascun elemento.

Possiamo definire la seguente classe:

'use strict';

class Sorter {
    constructor({ wrapper, sorters, elements, skipIndex, dataMap, keyMap }) {
        this.wrapper = wrapper;
        this.sorters = sorters;
        this.elements = elements;
        this.skipIndex = skipIndex;
        this.dataMap =  dataMap;
        this.keyMap =   keyMap;

        this.setUpElements();
        this.sort();
    }

    setDataType(element, type, key) {
        let text = element.innerText;
        let value = text;
        switch (type) {
            case 'date':
               value = Date.parse(text);
               break;
            case 'float':
                value = value.replace(/[^0-9\.]+/g, '');
                break;
            default:
                break;
        }
        element.parentNode.setAttribute('data-' + key, value);
    }

    sort() {
        const self = this;
        self.sorters.forEach(srtr => {
            let ascBtn = srtr.querySelector('[data-sort="asc"]');
            let descBtn = srtr.querySelector('[data-sort="desc"]');

            ascBtn.addEventListener('click', e => {
                e.preventDefault();
                self.sortElements(self.wrapper.querySelectorAll('tbody tr'), ascBtn.dataset.sort, ascBtn.parentNode.dataset.key, ascBtn.parentNode.dataset.type);
            }, false);

            descBtn.addEventListener('click', e => {
                e.preventDefault();
                self.sortElements(self.wrapper.querySelectorAll('tbody tr'), descBtn.dataset.sort, descBtn.parentNode.dataset.key, descBtn.parentNode.dataset.type);
            }, false);
        });
    }

    setUpElements() {
        const self = this;
        if(this.elements.length === 0) {
            return false;
        }
        self.sorters.forEach((sorter, i) => {
           sorter.setAttribute('data-type', self.dataMap[i]);
           sorter.setAttribute('data-key', self.keyMap[i]);
        });
        self.elements.forEach(element => {
            element.querySelectorAll('td').forEach((cell, index) => {
                if(index > self.skipIndex) {
                    cell.classList.add('sortable');
                }
            });
            element.querySelectorAll('.sortable').forEach((sortable, ind) => {
               self.setDataType(sortable, self.dataMap[ind], self.keyMap[ind]);
            });
        });
    }

    sortElements(elements, order, key, type) {
        const comparator = (a, b) => {
            let v1 = a.dataset[key];
            let v2 = b.dataset[key];

            if(type === 'integer' || type === 'date') {
                v1 = parseInt(v1, 10);
                v2 = parseInt(v2, 10);
            }

            if(type === 'float') {
                v1 = parseFloat(v1);
                v2 = parseFloat(v2);
            }

            if(order === 'asc') {
                return v1 - v2;
            } else {
                return v2 - v1;
            }
        };
        const items = Array.from(elements);
        const sorted = items.sort(comparator);

        sorted.forEach(el => {
           el.parentNode.appendChild(el);
        });
    }

}

Abbiamo impostato un evento click su ciascun pulsante di ordinamento presente nelle intestazioni dei dati. sortElements() ordina gli elementi in base alla chiave e al tipo di dati. La chiave viene utilizzata per accedere all'attributo di dati corretto, mentre il tipo ci consente di effettuare la conversione da stringa al tipo di dati di destinazione prima di effettuare la comparazione tra valori.

Possiamo usare la nostra classe in questo modo:

(function () {
        const sorter = new Sorter({
            wrapper: document.querySelector('.data'),
            sorters: document.querySelectorAll('.sort'),
            elements: document.querySelectorAll('.data tbody tr'),
            skipIndex: 0,
            dataMap: ['integer', 'integer', 'integer', 'integer', 'integer', 'integer', 'float', 'date'],
            keyMap: ['logicalwidth', 'logicalheight', 'physicalwidth', 'physicalheight', 'ppi', 'scalefactor', 'screendiagonal', 'release']
        });
    })();

Demo

JavaScript: sort elements

Torna su