问题
I have two scripts which work perfectly when a page contains a single table. However, now I have need to put multiple tables on the same page which support the same functions.
I need some help converting these two scripts to work with multiple tables on the same page, while maintaining the same functionality.
The first script is called "TABLE DATA STATES". The second script is called "SORT TABLE DATA".
Current JSBin: https://jsbin.com/noyoluhasa/1/edit?html,js,output
// ===================================================================
// =================== TABLE DATA STATES =============================
// ===================================================================
// Answer to my question on Stackoverflow:
// http://stackoverflow.com/questions/33128718/change-data-attribute-on-click-of-html-elements
// JsFiddle: http://jsfiddle.net/pya9jzxm/14
// Get all rows into the array except the <thead> row
var tbody = document.querySelector('tbody');
var trs = tbody.querySelectorAll('tr');
var tr, index = 0, length = trs.length;
// Start the loop
for (; index < length; index++) {
tr = trs[index];
// Set the attributes to default state
tr.setAttribute('data-state', 'enabled');
tr.setAttribute('data-display', 'collapsed');
tr.addEventListener('click',
function () {
// If its the row alphabet-label, skip it
if (this.classList.contains('alphabet-label')) {
return;
}
// Conditional logic to make the rows reset after clicking away from highlighted row
var trIndex = 0, trLength = trs.length, hasExpanded = false;
var state = 'disabled';
if (tbody.querySelectorAll('[data-display="expanded"]').length > 0) {
hasExpanded = true;
state = 'enabled';
}
for (; trIndex < trLength; trIndex++) {
// Set all rows to disabled on click of any row
trs[trIndex].setAttribute('data-state', state);
// Reset the display of all rows
trs[trIndex].setAttribute('data-display', 'collapsed');
}
if (!hasExpanded) {
// Set the clicked row to active highlighted state
this.setAttribute('data-state', 'enabled');
this.setAttribute('data-display', 'expanded');
}
}
);
}
// ===================================================================
// =================== SORT TABLE DATA ===============================
// ===================================================================
// For reference:
// this.setAttribute('data-state', this.getAttribute('data-state').contains === "enabled" ? "disabled" : "enabled");
// Adds icon to clicked <th>
// VanillaJS version - opted for jquery.tablesorter plugin due to flexibility and ease of use
var thsort = document.querySelectorAll('th')
//console.log(thsort);
var sort, sortIndex = 0, sortlength = thsort.length;
for (; sortIndex < sortlength; sortIndex++) {
sort = thsort[sortIndex];
//console.log(sort);
// On click to sort table column, do this:
sort.addEventListener('click',
function () {
var rm, rmIndex = 0;
for (; rmIndex < sortlength; rmIndex++) {
rmsort = thsort[rmIndex];
// Remove sort icon from other <th> elements
rmsort.classList.remove('sort-key');
// Add sort icon to this <th>
this.classList.add('sort-key');
//console.log(rmsort);
// Conditional logic to switch asc desc label
var state = 'asc', prevState = 'desc', hasAsc, prevState;
if (this.classList.contains('asc')) {
hasAsc = true;
state = 'desc';
prevState = 'asc';
//console.log(prevState);
}
// Set all rows to disabled on click of any row
this.classList.add(state);
this.classList.remove(prevState);
//if (hasAsc) {
// // Set the clicked row to active highlighted state
// this.setAttribute('class', state);
//}
}
}
);
}
I tried wrapping my codes in this code, in addition to replacing instances of tbody
with thisTable
, but then the scripts only worked for the last occurence of the table:
var alltables = document.querySelectorAll('tbody')
console.log(alltables);
var thisTable, sortIndex = 0, sortlength = alltables.length;
for (; sortIndex < sortlength; sortIndex++) {
thisTable = alltables[sortIndex];
// original code here
}
回答1:
So this is really just a scope issue. You are referencing tbody
and this NodeList of trs
in the event handlers but those values have changed over time because of multiple tables. When those handlers are called and it sees tbody
it first checks if that variable is part of its current scope, which it isn't. So it checks the next scope up until it finds it. But what it finds is the last value of that variable as it changed over time.
The easiest way to fix this is to surround your original block of code in a function, to give it scope when its called, and call that function passing the current table to it for each table. Then the only thing that function has in its scope is the table we care about and every variable we create within that function like trs
will be in the scope of only that specific function call.
Take a look at the code below and check out the fiddle and let me know if you have any questions about it. You can see I used your original idea of that loop of all tables only I found the tables based on the table
class, queried the table for its tbody
and passed that to our configureTable
function.
Fiddle: https://jsfiddle.net/rbpc5vfu/
ConfigureTable Function:
function configureTable (tbody) {
var trs = tbody.querySelectorAll('tr');
var tr, index = 0,
length = trs.length;
// Start the loop
for (; index < length; index++) {
tr = trs[index];
// Set the attributes to default state
tr.setAttribute('data-state', 'enabled');
tr.setAttribute('data-display', 'collapsed');
tr.addEventListener('click',
function() {
// If its the row alphabet-label, skip it
if (this.classList.contains('alphabet-label')) {
return;
}
// Conditional logic to make the rows reset after clicking away from highlighted row
var trIndex = 0,
trLength = trs.length,
hasExpanded = false;
var state = 'disabled';
if (tbody.querySelectorAll('[data-display="expanded"]').length > 0) {
hasExpanded = true;
state = 'enabled';
}
for (; trIndex < trLength; trIndex++) {
// Set all rows to disabled on click of any row
trs[trIndex].setAttribute('data-state', state);
// Reset the display of all rows
trs[trIndex].setAttribute('data-display', 'collapsed');
}
if (!hasExpanded) {
// Set the clicked row to active highlighted state
this.setAttribute('data-state', 'enabled');
this.setAttribute('data-display', 'expanded');
}
}
);
}
// ===================================================================
// =================== SORT TABLE DATA ===============================
// ===================================================================
// For reference:
// this.setAttribute('data-state', this.getAttribute('data-state').contains === "enabled" ? "disabled" : "enabled");
// Adds icon to clicked <th>
// VanillaJS version - opted for jquery.tablesorter plugin due to flexibility and ease of use
var thsort = tbody.querySelectorAll('th');
//console.log(thsort);
var sort, sortIndex = 0,
sortlength = thsort.length;
for (; sortIndex < sortlength; sortIndex++) {
sort = thsort[sortIndex];
//console.log(sort);
// On click to sort table column, do this:
sort.addEventListener('click',
function() {
var rm, rmIndex = 0;
for (; rmIndex < sortlength; rmIndex++) {
rmsort = thsort[rmIndex];
// Remove sort icon from other <th> elements
rmsort.classList.remove('sort-key');
// Add sort icon to this <th>
this.classList.add('sort-key');
//console.log(rmsort);
// Conditional logic to switch asc desc label
var state = 'asc',
prevState = 'desc',
hasAsc, prevState;
if (this.classList.contains('asc')) {
hasAsc = true;
state = 'desc';
prevState = 'asc';
//console.log(prevState);
}
// Set all rows to disabled on click of any row
this.classList.add(state);
this.classList.remove(prevState);
//if (hasAsc) {
// // Set the clicked row to active highlighted state
// this.setAttribute('class', state);
//}
}
}
);
}
}
Initialize tables on load:
var alltables = document.querySelectorAll('.table');
var thisTable, sortIndex = 0, sortlength = alltables.length;
for (; sortIndex < sortlength; sortIndex++) {
thisTable = alltables[sortIndex];
var tbody = thisTable.querySelector('tbody');
configureTable(tbody);
}
As you can see I didn't really change much if you look. I just wrapped your original code in a function block. Then stole your loop from above, found all the tables, for each table found its tbody and called our new function passing the tbody to it. Voila. Scope!
来源:https://stackoverflow.com/questions/34516684/convert-script-to-handle-multiple-tables-independently