summaryrefslogtreecommitdiff
path: root/static/js/sortTable.js
blob: 16e02e91cd1f3628e43231261776f854da155608 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Select the table and table headers.
var table = document.querySelector('#sitemapTable');
var headers = Array.from(table.querySelectorAll('th'));

// Create and append the live region for accessibility announcements.
var liveRegion = document.createElement('div');
liveRegion.setAttribute('aria-live', 'polite');
liveRegion.setAttribute('aria-atomic', 'true');
liveRegion.classList.add('visually-hidden');
document.body.appendChild(liveRegion);

// Initialise headers with click and keyboard listeners.
initializeHeaders();
addSortText(); // Add text for screen readers for initial sort direction.
updateSortIndicators(headers[0], 'asc'); // Set initial sort indicators.

function updateSortIndicators(header, direction) {
    removeSortArrows(header);
    var arrow = document.createElement('span');
    arrow.classList.add('sort-arrow');
    arrow.textContent = direction === 'asc' ? ' ▲' : ' ▼';
    arrow.setAttribute('aria-hidden', 'true');
    header.appendChild(arrow);
}

function removeSortArrows(header) {
    var arrows = header.querySelectorAll('.sort-arrow');
    arrows.forEach(function (arrow) {
        arrow.remove();
    });
}

function initializeHeaders() {
    headers.forEach(function (header, index) {
        header.classList.add('sortable');
        header.setAttribute('tabindex', '0');
        header.sortDirection = 'asc'; // Default sort direction.
        var sortAttribute = index === 0 ? 'ascending' : 'none';
        header.setAttribute('aria-sort', sortAttribute);
        header.addEventListener('click', function () {
            sortTable(index);
        });
        header.addEventListener('keydown', function (e) {
            if (e.key === 'Enter' || e.key === ' ') {
                e.preventDefault();
                sortTable(index);
            }
        });
    });
}

function announceSort(header, direction) {
    var columnTitle = header.querySelector('.columntitle').textContent;
    liveRegion.textContent =
        'Column ' + columnTitle + ' is now sorted in ' + direction + ' order';
}

function sortTable(index) {
    var header = headers[index];
    var direction = header.sortDirection === 'asc' ? 'desc' : 'asc';
    var tbody = table.querySelector('tbody');
    var rows = Array.from(tbody.querySelectorAll('tr'));
    sortRows(rows, index, direction);
    refreshTableBody(tbody, rows);
    updateHeaderAttributes(header, direction);
    announceSort(header, direction === 'asc' ? 'ascending' : 'descending');
}

function sortRows(rows, index, direction) {
    rows.sort(function (rowA, rowB) {
        var cellA = rowA.querySelectorAll('td')[index].textContent;
        var cellB = rowB.querySelectorAll('td')[index].textContent;
        return direction === 'asc'
            ? cellA.localeCompare(cellB)
            : cellB.localeCompare(cellA);
    });
}

function refreshTableBody(tbody, rows) {
    tbody.innerHTML = ''; // Clear existing rows.
    rows.forEach(function (row) {
        tbody.appendChild(row);
    });
}

function updateHeaderAttributes(header, direction) {
    headers.forEach(function (otherHeader) {
        if (otherHeader !== header) {
            otherHeader.setAttribute('aria-sort', 'none');
            removeSortArrows(otherHeader);
        }
    });
    header.setAttribute('aria-sort', direction === 'asc' ? 'ascending' : 'descending');
    header.sortDirection = direction;
    updateSortIndicators(header, direction);
    updateAnnounceText(header);
}

// Update screen reader text for sorting.
function updateAnnounceText(header) {
    var span = header.querySelector('.visually-hidden');
    span.textContent =
        'Click to sort in ' +
        (header.sortDirection === 'asc' ? 'descending' : 'ascending') +
        ' order';
}

// Add text for screen readers regarding sort order.
function addSortText() {
    headers.forEach(function (header) {
        var span = document.createElement('span');
        span.classList.add('visually-hidden');
        span.textContent = 'Click to sort in descending order';
        header.appendChild(span);
    });
}

headers[0].sortDirection = 'asc';
headers[0].setAttribute('aria-sort', 'ascending');