Tristin Baker
Tristin Baker

Reputation: 39

Is there a way around ASP's control ID (ctl00, ctl01, etc) breaking NVDA?

To preface this, I am new to ASP.

I'm working on the accessibility of our website and we have a Control with a list of radio buttons that are inside of a repeater. When using the screen reader NVDA, it reads each radio button as "1 of 1" instead of "1 of 3", "2 of 3" and "3 of 3".

I've diagnosed that the issue appears to be coming from the fact that ASP auto appends "ctl00", "ctl01", and "ctl02" to each radio button. I know that if I were to remove those and/or change them all to "ctl00", NVDA correctly reads them as intended.

Another quirk that I can't quite explain is that using Windows and Chrome with NVDA results in it being read correctly anyway, but any other browser I've tried on Windows and all browsers on Mac (with VoiceOver, not NVDA) do not read as intended.

Is there a way to remove the "ctl00" from the name attribute in the HTML?

Sample code (.ascx):

<div class="css-radio-buttons">
    <ul class="css-radio-buttons__ul">
        <asp:Repeater 
            ID="shippingOptionsRepeater" 
            runat="server" 
            OnItemDataBound="OnOptionBound">
            <ItemTemplate>
                <li class="css-radio-buttons__radio-item">
                    <asp:RadioButton 
                        ID="shippingOptionRadioButton" 
                        runat="server" 
                        OnCheckedChanged="OnOptionChoice"
                        CssClass="css-radio-buttons__input"
                    />
                </li>
            </ItemTemplate>
        </asp:Repeater>
    </ul>
</div>

Code behind:

protected void OnOptionBound(object sender, RepeaterItemEventArgs eventArgs)
{
    ShippingOption option = (ShippingOption)eventArgs.Item.DataItem;

    RadioButton radioButton = (RadioButton)eventArgs.Item.FindControl("shippingOptionRadioButton");
    // omitted
    radioButton.AutoPostBack = true;
    radioButton.GroupName = GROUP_NAME_PREFIX + option.FulfillerId.ToString();
    //omitted
}

Sample output:

<div class="css-radio-buttons">
    <ul class="css-radio-buttons__ul">
        
                <li class="css-radio-buttons__radio-item">
                    <span class="css-radio-buttons__input" data-fulfiller-id="1" data-option-id="3"><input id="ctlOrderReview_shipments_ctl00_shippingOptions_shippingOptionsRepeater_ctl00_shippingOptionRadioButton" type="radio" name="ctlOrderReview$shipments$ctl00$shippingOptions$shippingOptionsRepeater$ctl00$ShippingOptions1" value="shippingOptionRadioButton" checked="checked" onclick="setUniqueRadioButton('input[name$=&quot;ShippingOptions1&quot;]', this);"><label for="ctlOrderReview_shipments_ctl00_shippingOptions_shippingOptionsRepeater_ctl00_shippingOptionRadioButton">FREE Delivery Between Dec 25-29</label></span>
                </li>
            
                <li class="css-radio-buttons__radio-item">
                    <span class="css-radio-buttons__input" data-fulfiller-id="1" data-option-id="2"><input id="ctlOrderReview_shipments_ctl00_shippingOptions_shippingOptionsRepeater_ctl01_shippingOptionRadioButton" type="radio" name="ctlOrderReview$shipments$ctl00$shippingOptions$shippingOptionsRepeater$ctl01$ShippingOptions1" value="shippingOptionRadioButton" onclick="setUniqueRadioButton('input[name$=&quot;ShippingOptions1&quot;]', this);setTimeout('__doPostBack(\'ctlOrderReview$shipments$ctl00$shippingOptions$shippingOptionsRepeater$ctl01$shippingOptionRadioButton\',\'\')', 0)"><label for="ctlOrderReview_shipments_ctl00_shippingOptions_shippingOptionsRepeater_ctl01_shippingOptionRadioButton">$29.99 Delivery by Dec 24</label></span>
                </li>
            
                <li class="css-radio-buttons__radio-item">
                    <span class="css-radio-buttons__input" data-fulfiller-id="1" data-option-id="1">
<input id="ctlOrderReview_shipments_ctl00_shippingOptions_shippingOptionsRepeater_ctl02_shippingOptionRadioButton" type="radio" name="ctlOrderReview$shipments$ctl00$shippingOptions$shippingOptionsRepeater$ctl02$ShippingOptions1" value="shippingOptionRadioButton" onclick="setUniqueRadioButton('input[name$=&quot;ShippingOptions1&quot;]', this);setTimeout('__doPostBack(\'ctlOrderReview$shipments$ctl00$shippingOptions$shippingOptionsRepeater$ctl02$shippingOptionRadioButton\',\'\')', 0)"><label for="ctlOrderReview_shipments_ctl00_shippingOptions_shippingOptionsRepeater_ctl02_shippingOptionRadioButton">$39.99 Delivery by Dec 23</label></span>
                </li>
            
    </ul>
</div>

Upvotes: 1

Views: 373

Answers (1)

GrahamTheDev
GrahamTheDev

Reputation: 24825

It has been a long time since I touched ASP so if this is only 80% correct then my apologies.

With that being said you can override the automatic naming of radio buttons within a repeater using GroupName.

So in the following adjustment I simply added GroupName="shipping", this should then generate all radio buttons with the name="shipping" and it will then work as expected (I believe, yet again apologies it has been a looong time!).

<ItemTemplate>
            <li class="css-radio-buttons__radio-item">
                <asp:RadioButton 
                    ID="shippingOptionRadioButton" 
                    runat="server" 
                    OnCheckedChanged="OnOptionChoice"
                    CssClass="css-radio-buttons__input"
                    GroupName="shipping" 
                />
            </li>
        </ItemTemplate>

Upvotes: 0

Related Questions