【问题标题】:Convert script to handle multiple tables independently转换脚本以独立处理多个表
【发布时间】:2015-12-29 18:17:42
【问题描述】:

当一个页面包含一个表格时,我有两个脚本可以完美运行。但是,现在我需要在同一页面上放置多个支持相同功能的表。

我需要一些帮助来转换这两个脚本以在同一页面上处理多个表,同时保持相同的功能。

第一个脚本称为“TABLE DATA STATES”。 第二个脚本称为“SORT TABLE DATA”。

当前的 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);
                //}
            }
        }
    );
}

除了将tbody 的实例替换为thisTable 之外,我还尝试将我的代码包装在这段代码中,但是脚本仅适用于表格的最后一次出现:

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
}

【问题讨论】:

    标签: javascript loops foreach nodelist htmlcollection


    【解决方案1】:

    所以这实际上只是一个范围问题。您在事件处理程序中引用tbodytrs 的这个NodeList,但由于多个表,这些值随着时间的推移而变化。当调用这些处理程序并看到tbody 时,它首先检查该变量是否是其当前作用域的一部分,而事实并非如此。所以它会检查下一个作用域,直到找到它。但它发现的是该变量随时间变化的最后一个值。

    解决此问题的最简单方法是将您的原始代码块包含在一个函数中,在调用时为其提供范围,然后调用该函数,将每个表的当前表传递给它。那么该函数在其范围内的唯一内容就是我们关心的表,并且我们在该函数中创建的每个变量(如trs)都将仅在该特定函数调用的范围内。

    查看下面的代码并查看小提琴,如果您对此有任何疑问,请告诉我。你可以看到我使用了你对所有表循环的原始想法,只是我找到了基于 table 类的表,查询表的 tbody 并将其传递给我们的 configureTable 函数。

    小提琴:https://jsfiddle.net/rbpc5vfu/

    配置表功能:

    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);
                        //}
                    }
                }
            );
        }
    }
    

    加载时初始化表:

    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);
    }
    

    如你所见,如果你看的话,我并没有真正改变太多。我只是将您的原始代码包装在一个功能块中。然后从上面偷了你的循环,找到了所有的表,每个表都找到了它的 tbody 并调用了我们的新函数,将 tbody 传递给它。瞧。范围!

    【讨论】:

    • 老兄!谢谢!这很有意义,而且效果很好。非常感谢您的帮助!
    • 所以如果我理解正确,没有将代码包装在一个函数中,那么每次脚本找到一个新表时,所有的值都会改变,所以它只会引用它找到的表的最后一个实例.但是通过将代码包装在一个函数中,然后将每个表单独传递给该函数,我们通过为每个表创建一个范围来解决该问题,确保变量不会被其他表更改。感谢您的解释:-)
    • 没错。循环会随着时间的推移改变这些值。通过添加函数作用域,我们保证当这些事件处理程序查找这些变量的作用域时,它们仍然保持我们期望的值。乐意效劳!!很高兴它对你有用。
    猜你喜欢
    • 1970-01-01
    • 2021-10-31
    • 1970-01-01
    • 1970-01-01
    • 2022-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-07
    相关资源
    最近更新 更多