2

I am trying to achieve GridView Up and Down keyboard navigation feature, using jQuery. I have written code for the same, but with a bug that it is working only once.

Steps to reproduce the bug

  • After copying my sample code to your WebForm.aspx and WebForm.aspx.cs respectively, run the form
  • Click on the 2nd record (for eg.) to select the GridView record
  • Press down arrow key to select the next record
  • Press down arrow key again to select next record (but it won't work here)

Now, if you will click on any row again the down arrow key will work for once again.

Please indicate what I am missing here.

WebForm1.aspx

<head runat="server">
    <title></title>
    <style type="text/css">
        .normalRow
        {
            background-color: White;
            color: Black;
        }
        .selectedRowNew
        {
            background-color: #b0c4de;
            color: Black;
        }
    </style>

    <script src="js/jquery-1.6.1.min.js" type="text/javascript"></script>

    <script type="text/javascript">
        $(document).ready(function () {
            // The selector expression tr[id] will ensure the click event is added to only data rows since we have added id attribute 
            // to only data rows from code behind

            $('#<%=GridView1.ClientID %> tr[id]').click(function () {
                $('#<%=GridView1.ClientID %> tr[id]').removeClass("selectedRowNew").addClass("normalRow");
                $(this).removeClass("normalRow").addClass("selectedRowNew");
            });

            $('#<%=GridView1.ClientID %> tr[id]').mouseover(function () {
                $(this).css({ cursor: "default", cursor: "default" });
            });

            $('#<%=GridView1.ClientID %> tr[id]').keydown(function (event) {
                if (event.keyCode == "40") {
                    $('#<%=GridView1.ClientID %> tr[id]').removeClass("selectedRowNew").addClass("normalRow");

                    var row = $(this).closest('tr');
                    var next = row.next();
                    next.removeClass("normalRow").addClass("selectedRowNew");
                    next.focus();
                    next.click();
                }
            });

        });

    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:GridView ID="GridView1" runat="server" OnRowDataBound="GridView1_RowDataBound">
        </asp:GridView>
    </div>
    </form>
</body>

WebForm1.aspx.cs

protected void Page_Load(object sender, EventArgs e)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("Id", typeof(Int32));
            dt.Columns.Add("Name", typeof(String));

            dt.Rows.Add(new object[] { 1, "John" });
            dt.Rows.Add(new object[] { 2, "James" });
            dt.Rows.Add(new object[] { 3, "Christine" });
            dt.Rows.Add(new object[] { 4, "Michael" });
            dt.Rows.Add(new object[] { 5, "David" });
            dt.Rows.Add(new object[] { 6, "Susan" });

            GridView1.DataSource = dt;
            GridView1.DataBind();
        }

        protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e) 
        {
            if (e.Row.RowType == DataControlRowType.DataRow) 
            {
                e.Row.Attributes.Add("id", "0");
            }
        }

(updated js code) - still not working although stepping through it properly

<script type="text/javascript">
        $(document).ready(function () {
            // The selector expression tr[id] will ensure the click event is added to only data rows since we have added id attribute 
            // to only data rows from code behind

            $('#<%=GridView1.ClientID %> tr[id]').click(function () {
                $('#<%=GridView1.ClientID %> tr[id]').removeClass("selectedRowNew").addClass("normalRow");
                $(this).removeClass("normalRow").addClass("selectedRowNew");
            });

            $('#<%=GridView1.ClientID %> tr[id]').mouseover(function () {
                $(this).css({ cursor: "default", cursor: "default" });
            });

            // @freshbm: your code goes here
            $("body").keydown(function (e) {
                if (e.keyCode == 40) //down arrow key code
                    toggleRowSelectionDown();
                if (e.keyCode == 38) // up arrow key code
                    toggleRowSelectionUp();
            }); //this code detect is it up or down arrow

            function toggleRowSelectionDown() {
                var row = $("<%=GridView1.ClientID%> .selectedRowNew");
                if (!row.is(":last-child")) { //check for last row in grid
                    $("<%=GridView1.ClientID%> tr[id]").removeClass("selectedRowNew").addClass("normalRow");
                    var next = row.next();
                    next.removeClass("normalRow").addClass("selectedRowNew");
                }
            }

            function toggleRowSelectionUp() {
                var row = $("<%=GridView1.ClientID%> .selectedRowNew");
                if (!row.is(":last-child")) { // check for first row in grid
                    $("<%=GridView1.ClientID%> tr[id]").removeClass("selectedRowNew").addClass("normalRow");
                    var prev = row.prev();
                    prev.removeClass("normalRow").addClass("selectedRowNew");
                }
            }
        });
    </script>
Khadim Ali
  • 2,548
  • 3
  • 33
  • 61

2 Answers2

2

I've figured out your problem, you can't bind keydown on table row. But you can add listener to body for keydown:

$("body").keydown(function(e){
  if(e.keyCode == 40 ) //down arrow key code
    toggleRowSelectionDown();
  if(e.keyCode == 38) // up arrow key code
    toggleRowSelectionUp(); 
}); //this code detect is it up or down arrow

I have put your code in functions for easy reading and maintenance:

function toggleRowSelectionDown() {
    var row = $("#<%=GridView1.ClientID%> .selectedRowNew");
    if (!row.is(":last-child")) { //check for last row in grid
        $("#<%=GridView1.ClientID%> tr[id]").removeClass("selectedRowNew").addClass("normalRow");
        var next = row.next();
        next.removeClass("normalRow").addClass("selectedRowNew");
    }
}

function toggleRowSelectionUp() {
                var row = $("#<%=GridView1.ClientID%> .selectedRowNew");
                if (!row.is(":first-child")) { // check for first row in grid
                    var prev = row.prev();
                    if (prev.attr('id')) { // to avoid header row
                        $("#<%=GridView1.ClientID%> tr[id]").removeClass("selectedRowNew").addClass("normalRow");
                        prev.removeClass("normalRow").addClass("selectedRowNew");
                     }

                }
            }

I created this jsfiddle to demonstrate functionality: http://jsfiddle.net/Ps3WL/31/

Added check for start and end of the grid

Khadim Ali
  • 2,548
  • 3
  • 33
  • 61
freshbm
  • 5,540
  • 5
  • 46
  • 75
  • Have you tested the code? It still not working at all. The program execution steps through the js code properly but doesn't reflect the changes on screen. I am testing on IE8 and FF19 with jQuery 1.6.1. Does it matter? Moreover, how could you say that keydown can't be binded upon table row, as in my posted sample it is working for atleast first time? – Khadim Ali Feb 22 '13 at 12:21
  • ok, i've forgot to add # - id selector in my code! I'll update it, then you try – freshbm Feb 22 '13 at 13:08
  • Oh no...! :). I must have noticed that. Anyway, thank you so much for your efforts. However, I am still confused why the keydown even was not binding with the table row?!!!! – Khadim Ali Feb 22 '13 at 13:27
  • 1
    Ok, i've found this " The keydown event is sent to an element when the user first presses a key on the keyboard. It can be attached to any element, but the event is only sent to the element that has the focus. " on http://api.jquery.com/keydown/ and by this post http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus can't have focus – freshbm Feb 22 '13 at 13:36
  • Ok, then I guessed it will work while binding keydown event with the GridView itself. And yess, it is. Thanks again @freshbm. – Khadim Ali Feb 22 '13 at 13:53
0

you can replace your line with my lines only for

(e.keycode==40)            $(this).closest('tr').next().find('td:eq('+$(this).closest('td').index()+')').find('input').focus();
vikas
  • 336
  • 2
  • 5
  • 16