65.9K
CodeProject 正在变化。 阅读更多。
Home

在 JavaScript 和 CSS 中高亮 GridView 行

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (4投票s)

2015年10月7日

CPOL

5分钟阅读

viewsIcon

17028

使用 JavaScript 管理控件布局、格式和突出显示,无需进行 Postback,这样更加简洁。

引言

有很多关于如何在 GridView 或其他与表相关的 HTML 控件中突出显示(背景颜色)行的示例和文章。

这篇有什么不同?首先,我展示的示例和代码是纯 JavaScript(不是 jQuery,因为那是另一个层级的专业知识!),代码是动态的(无论控件 ID 是什么都可以使用),没有硬编码控件 ID,JavaScript 位于主文件中(意味着任何辅助 ASPX 页面都可以重用相同的 JS 代码而无需在单个页面中重复),最后,我利用 CSS(样式表)的力量来控制背景颜色,而不会丢失之前的背景效果(就像一个 CSS 层覆盖另一个 CSS)。

为了实现本文的成果,没有使用一行代码隐藏或 C#/VB 代码。此外,对于那些学习 JavaScript 和客户端功能强大之处的人来说,这是一个很好的起点。

背景

好几天我都在苦思冥想如何避免 PostBack(可怕的闪烁!)。一旦你触摸了任何东西,无论是复选框、文本框还是链接,屏幕都会**闪烁**,因为调用会通过服务器并返回。肯定有更好的办法!

是的,有。

这仅仅是 JavaScript 或客户端编程。你能在客户端实现的、无需 Postback 的功能越多越好。你可以减少数据“默认”的风险,即表单上的数据重置为第一次加载时的原始状态,并且可以减少在服务器上控制事件流的风险;因为所有事件都在客户端浏览器中“平坦”地运行。代码会立即直接在你的网站中运行。无需调试!

在我看来,至少学习一些 JavaScript 对于任何网页设计师来说都是必须的。

尽情享用!

使用代码

那么,我们开始吧。

当然,第一件事就是你常用的 GridView 控件。我不会详细介绍如何在设计时创建它或如何用数据加载它等等。我会让你来管理 <datasource> 或与 GridView 相关的任何其他属性。

简单来说,这里有一个简单的 GridView,以及一个**嵌套的 GridView** 控件,在本例中它与主 GridView 中提到的学生相关 - gvStudents。

<asp:GridView ID="gvStudents" runat="server" CssClass="gvCSS" DataSourceID="SqlDataSourceStudents" AutoGenerateColumns="False" Width="87%"
    OnRowDataBound="gvStudents_RowDataBound" OnDataBound="gvStudents_DataBound">
    <HeaderStyle BackColor="#5D7B9D" ForeColor="White" />
    <Columns>
        <asp:TemplateField>
            <HeaderTemplate>
                <asp:CheckBox id="CheckBoxAll" runat="server" onClick="gridViewCheckAll(this)" Text="Student" TextAlign="Left"/>
            </HeaderTemplate>
            <ItemTemplate>
                <asp:CheckBox ID="CheckBoxAdd" runat="server" onClick="highlightRow(this)" Visible='<%# Eval("StudentMobilePhone").ToString().Length > 0 %>'/>
            </ItemTemplate>
            <HeaderStyle Wrap="False" Width="200px" HorizontalAlign="Center" />
            <ItemStyle HorizontalAlign="Center"  Width="200px" Wrap="False" />
        </asp:TemplateField>
...
...
    </Columns>
</asp:GridView>

这里需要注意的主要几点是

  • 复选框的 onClick() 事件调用及其函数名称(嵌套 GridView 的将与此相同),
  • 每个 GridView 的 CssClass 属性(这些不会相同!),
  • 没有指定 AlternateRow 或 Row 格式,即使有,你也会在下面的 CSS 样式表引用中看到。HeaderStyle 仅用于标题行;并且,
  • 为了进一步“用户友好”,我实现了一个动态的 Visible 属性,根据学生是否有手机号码来决定是否显示复选框。至于它是否是一个有效号码,这超出了本文的范围。

嵌套的 GridView 同样相似,并嵌入在一个 <TemplateField> 或列中。

        <asp:TemplateField>
            <HeaderTemplate>
                <asp:CheckBox ID="cbNOKall" runat="server" Text="Next Of Kin" TextAlign="Left" onClick="gridViewCheckAll(this)"/>
            </HeaderTemplate>
            <ItemTemplate>
                <asp:GridView ID="gvNOKs" runat="server" CssClass="gvCSSsub" AutoGenerateColumns="False" BorderStyle="None" GridLines="None" ShowHeader="false" ShowFooter="false" BackColor="transparent" >
                    <Columns>
                        <asp:TemplateField HeaderText="Given Name" >
                            <ItemTemplate>
                                <asp:Label ID="lblNOKGivenName" runat="server" Text='<%# Eval("NOKname") %>'></asp:Label>
                            </ItemTemplate>
                            <ControlStyle Width="150px" />
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="NoK Type" >
                            <ItemTemplate>
                                <asp:Label ID="lblNOKType" runat="server" Text='<%# Eval("NOKType") %>'></asp:Label>
                            </ItemTemplate>
                            <ControlStyle Width="100px" />
                            <ItemStyle HorizontalAlign="Center" />
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="Mobile" >
                            <ItemTemplate>
                                <asp:Label ID="lblNOKMobile" runat="server" Text='<%# Eval("NOKMobile") %>'></asp:Label>
                            </ItemTemplate>
                            <ControlStyle Width="100px" />
                            <ItemStyle HorizontalAlign="Center" />
                        </asp:TemplateField>
                        <asp:TemplateField>
                            <ItemTemplate>
                                <asp:CheckBox ID="cbNOKAdd" runat="server" onClick="highlightRow(this)" Visible='<%# Eval("NOKMobile").ToString().Length > 0 %>'/>
                            </ItemTemplate>
                        </asp:TemplateField>
                   </Columns>
                </asp:GridView>
            </ItemTemplate>
            <HeaderStyle HorizontalAlign="Left" Wrap="False" />
            <ItemStyle HorizontalAlign="Center" Wrap="False" />
        </asp:TemplateField>

这里需要注意的主要几点是

  • 复选框的 onClick() 事件调用及其函数名称与 gvStudents 相同。
  • CssClass 属性名称不同;并且,
  • 同样没有指定 AlternateRow 或 Row 格式。

嵌套 GridView 的 CssClass 名称不同,这样我们可以将其行突出显示为**绿色**,**而不是黄色**。同时请注意透明背景?这样,如果学生在 NOK GridView 后面被选中,你仍然可以看到一层薄薄的黄色背景。

好的。到目前为止,我们有一个简单的 Gridview,在 NOK(Next of Kin)列中有一个嵌套的 Gridview,它有自己的主行和基于行的复选框。

所以,正如你可能已经猜到的,我们正在尝试做的是向用户提供数据,让他们可以使用复选框选择,无论是单个行还是所有行(看看 HeaderTemplates)。

快点展示 JavaScript!

如前所述,本文的另一个方面是使 JavaScript 代码**模块化**或只保存在一个地方,以便其他 ASPx 页面可以使用它。奇怪的是,我没有看到很多 JS 程序员这样做,而是到处乱放重复的代码!?为什么?你们在学校没学过 3G 编程吗?;-)

所以,废话不多说,将以下代码放在主页(如果你需要知道主页是什么 - 点击 这里)的 <body> 和 <form> 标签内,或者放在 <ContentPlaceHolder> 标签内。不要放在 <head> 元素内,否则子程序将无法拾取!如果你没有主页,请将其放在你的 ASPx 页面内的 <div>、<form> 或 <ContentPlaceHolder> 标签内。

现在,在你查看脚本之前,请注意这里有很多内容实际上比技术上必需的要多。然而,我包含了一些**垫片**(shims)以允许 JavaScript 在所有浏览器中运行,包括 7 及以上版本的旧版 IE。只需按原样复制它们。我在这里不解释它们的工作原理,但你可以搜索 ECMA262-5 例如,找到官方规范。

与本文相关的实际 JS 代码在注释掉的“hash-line”下方。

    <script type="text/javascript" >
        // Add ECMA262-5 method binding if not supported natively
        //
        if (!('bind' in Function.prototype)) {
            Function.prototype.bind= function(owner) {
                var that= this;
                if (arguments.length<=1) {
                    return function() {
                        return that.apply(owner, arguments);
                    };
                } else {
                    var args= Array.prototype.slice.call(arguments, 1);
                    return function() {
                        return that.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments)));
                    };
                }
            };
        }

        // Add ECMA262-5 string trim if not supported natively
        //
        if (!('trim' in String.prototype)) {
            String.prototype.trim= function() {
                return this.replace(/^\s+/, '').replace(/\s+$/, '');
            };
        }

        // Add ECMA262-5 Array methods if not supported natively
        //
        if (!('indexOf' in Array.prototype)) {
            Array.prototype.indexOf= function(find, i /*opt*/) {
                if (i===undefined) i= 0;
                if (i<0) i+= this.length;
                if (i<0) i= 0;
                for (var n= this.length; i<n; i++)
                    if (i in this && this[i]===find)
                        return i;
                return -1;
            };
        }
        if (!('lastIndexOf' in Array.prototype)) {
            Array.prototype.lastIndexOf= function(find, i /*opt*/) {
                if (i===undefined) i= this.length-1;
                if (i<0) i+= this.length;
                if (i>this.length-1) i= this.length-1;
                for (i++; i-->0;) /* i++ because from-argument is sadly inclusive */
                    if (i in this && this[i]===find)
                        return i;
                return -1;
            };
        }
        if (!('forEach' in Array.prototype)) {
            Array.prototype.forEach= function(action, that /*opt*/) {
                for (var i= 0, n= this.length; i<n; i++)
                    if (i in this)
                        action.call(that, this[i], i, this);
            };
        }
        if (!('map' in Array.prototype)) {
            Array.prototype.map= function(mapper, that /*opt*/) {
                var other= new Array(this.length);
                for (var i= 0, n= this.length; i<n; i++)
                    if (i in this)
                        other[i]= mapper.call(that, this[i], i, this);
                return other;
            };
        }
        if (!('filter' in Array.prototype)) {
            Array.prototype.filter= function(filter, that /*opt*/) {
                var other= [], v;
                for (var i=0, n= this.length; i<n; i++)
                    if (i in this && filter.call(that, v= this[i], i, this))
                        other.push(v);
                return other;
            };
        }
        if (!('every' in Array.prototype)) {
            Array.prototype.every= function(tester, that /*opt*/) {
                for (var i= 0, n= this.length; i<n; i++)
                    if (i in this && !tester.call(that, this[i], i, this))
                        return false;
                return true;
            };
        }
        if (!('some' in Array.prototype)) {
            Array.prototype.some= function(tester, that /*opt*/) {
                for (var i= 0, n= this.length; i<n; i++)
                    if (i in this && tester.call(that, this[i], i, this))
                        return true;
                return false;
            };
        }
        
        function hasClass(ele,cls) {
          return !!ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
        }

        function addClass(ele,cls) {
          if (!hasClass(ele,cls)) ele.className += " "+cls;
        }

        function removeClass(ele,cls) {
          if (hasClass(ele,cls)) {
            var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
            ele.className=ele.className.replace(reg,' ');
          }
        }

        //################################################################################################################
        function gridViewCheckAll(chk) {
          var cell = chk.parentNode;
          var row = cell.parentNode;
          var tbody = row.parentNode;
          var rows = tbody.children;

          var index = [].indexOf.call(row.children, cell);
          
          for (var i = 1; i < rows.length; i++) {
            var checkBoxes = rows[i].children[index].querySelectorAll('input[type=checkbox]');
            
            for (var j = 0; j < checkBoxes.length; j++) {
                checkBoxes[j].checked = chk.checked;
                highlightRow(checkBoxes[j]);
            }
          }
        }
        
        function highlightRow(chk) {
          var row = chk.parentNode.parentNode;  //go to the <td> of the checkbox and then again to the <tr> of the <td>, which is the row
          if (chk.checked)
            addClass(row, 'selected-row');
          else
            removeClass(row, 'selected-row');
        }
    </script>

 

最后一个要素是 CSS 样式表本身。

    <style type="text/css">
        .arial { font-family: Arial; font-size: small; }
        .table { font-family: Arial; font-size: small; background-color: #eeeeee; margin-right: 10px; display: inline; vertical-align: top; }
        .header { text-align: center; background-color: #5D7B9D; color: White; font-weight: bold; font-size: medium; }
        
        table.gvCSS {
          margin-top: 50px;
          font: 12px Verdana;
        }
          
        table.gvCSS > tbody > tr {
          background-color: white;
        }

        table.gvCSS > tbody > tr:nth-of-type(odd) {
          background-color: #EEE;
        }

        table.gvCSS > tbody > tr:first-of-type {
          background-color: #5D7B9D;
          color: white;
        }

        table.gvCSS > tbody > tr.selected-row {
          background-color: yellow;
        }

        table.gvCSSsub > tbody > tr.selected-row {
          background-color: lightgreen;
        }

        table.gvCSS tr td {
          padding: 4px 15px;
        }

        /* ******** */

        #gvCSS > tbody > tr > td:nth-of-type(1) {
          width: 100px;
        }

        #gvCSS > tbody > tr > td:nth-of-type(2) {
          width: 70px;
        }

        #gvCSS table > tbody > tr > td:nth-of-type(1) {
          width: 120px;
        }

        #gvCSS table > tbody > tr > td:nth-of-type(2) {
          width: 100px;
        }

        #gvCSS table > tbody > tr > td:nth-of-type(3) {
          width: 100px;
        }
    </style>

你会注意到 CSS 中有 **nth-of-type(odd)** 和 **nth-of-type** 对 <td> 标签的引用?

这就是设置 GridView 的 AlternateRow 背景颜色的方法!啊,我听见你说!

所以,上述 JavaScript 的优点是,一旦你选择了一行,该行就会突出显示为黄色(或嵌套 GridView 的行为绿色),如果你取消勾选复选框,原始的 AlternateRow 背景颜色就会恢复。干净利落。

关注点

总而言之,我们有一个“又一个如何突出显示表格行中某一行”的文章,但增加了一些“用户友好”的代码。大多数其他示例都在 JavaScript 代码中**硬编码**了 GridView 的 ID,我非常讨厌这一点,而且这通常是糟糕的编程实践。所有控件和 Web 标签的 ID 都可以收集并传递给一个函数,为什么不使用呢!?当然,我也可以将 JS 代码保存在一个 .js 文件中,并在每个 ASPx 页面上简单地引用它。是的,这是另一种方法,尤其是在你没有主页的情况下。

希望你觉得这很有用,特别是用于为旧版 IE 浏览器覆盖格式的 JS 脚本。

历史

2015 年 10 月:版本 1.0, 1.1(重新措辞和语法修正)

© . All rights reserved.