Melanie
Melanie

Reputation: 3111

querySelectorAll returns empty nodelist

I have an ASP.Net page with 3 textboxes and 1 radiobuttonlist. Each of the 4 controls has

class="tabbable"

in its definition. Here's the complete markup:

<%@ Control Language="c#" AutoEventWireup="false" Codebehind="approvalacctprocess.ascx.cs" Inherits="cmc.workflow.ui.ApprovalAcctProcess" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %>
<%@ Register tagprefix="CMC" Tagname="ApprovalComments" src="~/workflow\ui\ApprovalComments.ascx" %>
<script src="../../Scripts/jquery-1.4.1.js"></script>
<asp:Panel ID="pnlApprovalAC" CssClass="STDPANEL" HorizontalAlign="Center" Runat="server" Width="550">
    <TABLE cols="2" width="520">
        <TR>
            <TD class="FLDLABEL" style="VERTICAL-ALIGN: top">Client Number</TD>
            <TD>
                <asp:TextBox id=txtclnum style="VERTICAL-ALIGN: top" Width="300" Runat="server" CssClass="FLDVALUE" TabIndex="0" onchange="MoveNext(this);" Text='<%# Property["clnum"] %>' MaxLength="14" AutoPostBack="True" class="tabbable"></asp:TextBox>&nbsp;
                <asp:RegularExpressionValidator id="rxClNum" ValidationExpression="^[0-9]+[&#9;]*$|Clt Number TBD" ErrorMessage="Client Number consists of up to 14 numbers"
                    ControlToValidate="txtclnum" runat="Server"></asp:RegularExpressionValidator></TD>
        <TR>
            <TD class="FLDLABEL" style="VERTICAL-ALIGN: top">Matter Number (5-6 digit)</TD>
            <TD>
                <asp:Label id=lbclnum style="TEXT-ALIGN: right" Width="140" Runat="server" Text='<%# Property["clnum"] %>' Font-Name="verdana" Font-Size="x-small">
                </asp:Label>-
                <asp:TextBox id=txtmmatter Width="150" Runat="server" CssClass="FLDVALUE" TabIndex="1" Text='<%# Property["mmatter"] %>' MaxLength="6" AutoPostBack="True" class="tabbable"></asp:TextBox>&nbsp;
            </TD>
        <TR>
            <TD colSpan="2">
                <HR style="COLOR: gray; TEXT-ALIGN: left" SIZE="1">
            </TD>
        </TR>
        <tr>
            <td class="FLDLABEL" style="VERTICAL-ALIGN: top" width="500" colspan="2"><asp:Label runat="server" ID="lbExistingClientQuestion" Text="Is there an Engagement Letter on file for this client?" Visible="false" />&nbsp;&nbsp;
        <asp:Label runat="server" ID="lbUDFRetainerLetter" Text='<%# Property["RetainerLetter"] %>' Visible="false" /></td>
        </tr>
        <TR>
            <TD class="FLDLABEL" style="VERTICAL-ALIGN: top" width="500" colSpan="2">Has a
                retainer/engagement letter been submitted and approved by Charlotte Fischman?</TD>
        </TR>
        <TR>
            <TD colSpan="2">
                <asp:RadioButtonList id="rblRetLtrReturned" TabIndex="2" Runat="server" CssClass="RADIOBUTTONLIST" RepeatDirection="Horizontal" class="tabbable"
                    RepeatLayout="Table" RepeatColumns="1" width="300" AutoPostBack="True">
                    <asp:ListItem Value="0">No</asp:ListItem>
                    <asp:ListItem Value="1">Yes</asp:ListItem>
                </asp:RadioButtonList>
                <asp:Label id="lblnoretainerltrneeded" Runat="server" CssClass="SMALLNOTE" Text="(This is an existing client and the matter is in an existing area of law.&#13;&#10;&#9;&#9;&#9;&#9;&nbsp;&nbsp; A retainer letter may not be needed.)"
                    Font-Size="xx-small" Visible="False" ForeColor="red"></asp:Label></TD>
        </TR>
        <TR>
            <TD colSpan="2">
            </TD>
        </TR>
        <TR>
            <td class="FLDLABEL" style="VERTICAL-ALIGN: top" colSpan="2" width="500">Reason for Not Submitting an Retainer/Engagement Letter for Approval<SPAN style="FONT-WEIGHT: bold; COLOR: red">
                                *</SPAN>&nbsp;
            <asp:Label runat="server" CssClass="SMALLNOTE" Text="(Required if no retainer letter submitted and not an existing client)" Font-Size="XX-Small" ForeColor="Red" /></td>
        </TR>
        <TR>
            <td colspan="2">
                <asp:TextBox Width="500" runat="server" TextMode="MultiLine" TabIndex="3" CssClass="FLDVALUE" ID="txtReason" MaxLength="500" Text='<%# Property["Reason"] %>' AutoPostBack="True" class="tabbable" />

            </td>
        </TR>
        <TR>
            <TD colSpan="2">
                <HR style="COLOR: gray; TEXT-ALIGN: left" SIZE="1">
            </TD>
        </TR>
        <TR>
            <TD class="FLDLABEL" style="VERTICAL-ALIGN: top">Comments</TD>
            <TD>
                <asp:TextBox id="txtComments" style="VERTICAL-ALIGN: top" Width="300" TabIndex="4" Runat="server" CssClass="FLDVALUE"
                    MaxLength="450" Rows="5" TextMode="MultiLine" Text='<%# Property["AcctgComment"] %>' AutoPostBack="True" class="tabbable"></asp:TextBox></TD>
        </TR>
    </TABLE>
    <TABLE class="STDPANEL" style="VERTICAL-ALIGN: middle" height="50" width="550">
        <TR>
            <td align="center">
                <input id="btnSaveACProperty" runat="server" name="btnSaveACProperty" 
                    onserverclick="OnSave_Click" type="submit" value="Save Status and Comment">
                    &nbsp;&nbsp;
                    <input id="btnResetACProperty" runat="server" name="btnResetACProperty" 
                        type="reset" value="Cancel">
                    </input>
                </input>
            </td>
        <tr>
            <td align="left">
                <asp:ValidationSummary ID="valsum" runat="server" BorderColor="" 
                    BorderStyle="Solid" BorderWidth="2" CssClass="VALIDATORSUM" DisplayMode="List" 
                    Font-Bold="True" ForeColor="red" 
                    HeaderText="  Some errors occurred in your input.  Please correct them:&lt;br&gt; " 
                    ShowMessageBox="True" ShowSummary="True" Width="500" />
            </td>
        </tr>
    </TABLE>
</asp:Panel>
<script type="text/vbscript">

</script>
<script type="text/javascript" lang="javascript">
    function MoveNext(ele) {
        $(document).ready(function () {
            var lastTabIndex = 4;
            var currentElementID = ele.id; // ID set by OnFocusIn
            var currentElement = document.getElementById(currentElementID);
            var curIndex = currentElement.tabIndex; //get current elements tab index
            if (curIndex == lastTabIndex) { //if we are on the last tabindex, go back to the beginning
                curIndex = 0;
            }
            var tabbables = document.querySelectorAll("tabbable"); //get all tabbable elements
            alert(tabbables.length);
            for (var i = 0; i < tabbables.length; i++) { //loop through each element
                if (tabbables[i].tabIndex == (curIndex + 1)) { //check the tabindex to see if it's the element we want
                    tabbables[i].focus(); //if it's the one we want, focus it and exit the loop
                    break;
                }
            }
        });
    }
</script>

The textbox txtclnum calls the javascript function MoveNext at the bottom of the page (just to make sure everything loads in the right order)(this is taken from the first answer to this question). MoveNext has an alert in it to tell me what tabbables.length is. The alert returns 0 because the CssClass in the .Net controls overwrites the class="tabbable" in the HTML. I've tried

var tabbables = document.getElementsByTagName("*");

which gets me to the correct control, but the focus doesn't stay on that control. How can I keep the focus on the control?

Upvotes: 2

Views: 19652

Answers (4)

Melanie
Melanie

Reputation: 3111

OK, I feel like an idiot. Just for others' future reference, the problem was that 1) there's a Cssclass assigned to those controls, which overrides my tabbable class in the HTML markup; and 2) once I took care of that (see below code), it still wasn't working because (duh) each control had an AutoPostBack=true on it (they used to have server-side code attached to them). Now that AutoPostBack is false, my code works fine. Here it is:

<script type="text/javascript" lang="javascript">
function MoveNext(currentElement, btnID) {
    $(document).ready(function () {
        var saveButton = document.getElementById(btnID);
        saveButton.disabled = false;
        var lastTabIndex = 4;
        var curIndex = currentElement.tabIndex; //get current elements tab index
        if (curIndex == lastTabIndex) { //if we are on the last tabindex, go back to the beginning
            curIndex = 0;
        }
        var tabbables = document.getElementsByTagName("*"); //get all tabbable elements
        for (var i = 0; i < tabbables.length; i++) { //loop through each element
            if (tabbables[i].tabIndex != null & tabbables[i].tabIndex == (curIndex + 1)) { //check the tabindex to see if it's the element we want
                tabbables[i].focus(); //if it's the one we want, focus it and exit the loop
                break;
            }
        }
    });
}

Thanks to everyone for their help.

Upvotes: 2

elzi
elzi

Reputation: 5692

The way you're using it, document.querySelectorAll("tabbable") is looking for an element of the tag <tabbable>. Since it looks like you're trying to query by a class, add the period to denote it is such.

document.querySelectorAll(".tabbable")

When you use document.getElementsByClassName("tabbable") it could work, so I could see where you could get confused if you've used that method in the past.

Upvotes: 1

orion11
orion11

Reputation: 425

Since tabbable is a class, you need to put a period in front of it in your queryselector, so it should be:

document.querySelectorAll(".tabbable")

Edit: Just further clarification, the queryselector without the "." would be looking for html tags like <tabbable>. Since that does not exist, the length returned is 0.

Upvotes: 1

Quinto
Quinto

Reputation: 333

That function takes as its argument a CSS selector, so if you're looking for elements with class "tabbable" you would use document.querySelectorAll(".tabbable")

Upvotes: 9

Related Questions