I'm trying to improve accessibility on a site and I can't seem to solve a problem with NVDA screen reader and loading the results of a search page.
I've created a StackBlitz example which uses Angular and AG-Grid.
The result grid is not initially visible. When I load the results for the first time, it reads out the screen-reader-only texts, then it starts reading the whole table. I would prefer if the table was not read out loud, but that's not the issue.
The problem is when I load results again, simulating another search, the reader behaves very weird. It reads something random, and says numbers like 1 1 3... which I assume are part of the pagination. Sometimes it reads the table again, sometimes it doesn't.
On the search form I'm using aria-controls="queryResultRegion" and the target has role="region" and aria-live="polite" to make the reader aware that there's dynamic content
<form action="." aria-controls="queryResultRegion">
<button (click)="loadResults()">Load result</button>
<button (click)="noResult()">Load no result</button>
</form>
<div role="region" aria-live="polite" id="queryResultRegion">
<my-grid [rowData]="rowData"></my-grid>
</div>
On the result component I the class="sr-only" elements conditionally say either the number of results or just "No results".
<ng-container *ngIf="rowData">
<h2 class="sr-only" #resultHeader>Search Result</h2>
<div *ngIf="rowData.length < 1; else elseBlock" class="sr-only">
No results
</div>
<ng-template #elseBlock>
<div class="sr-only">
Number of results: {{ rowData.length }}
</div>
</ng-template>
<ag-grid-angular style="width: 100%; height: 200px;" class="ag-theme-alpine" [rowData]="rowData"
[columnDefs]="columnDefs" [gridOptions]="gridOptions">
</ag-grid-angular>
</ng-container>
Are you sure that a live region is the right thing for the results table?
I can understand the use of a live region to tell you "X results" or "No results", but not for the results themselves, which (I would expect) would be a browsable list containing a brief abstract and hyperlink for each entry.
If you mark up your results table correctly (with rowheaders and column headers) NVDA and other ATs will be able to browse it using special keyboard shortcuts. No need for a live region around it. This in itself might resolve (although not explain) your problem with 'weird' announcements.
A few more points:
aria-controls is not that useful. The spec gives no idea how it should actually behave, although the semantic is clear enough. AT support for it is currently poor. On the other hand, it does no actual harm, and makes a semantic relationship explicit. So... while I recommend that you keep it, don't expect it to do much (yet) either. I hope the community will work out a proper interaction pattern for aria-controls, because it would be handy in cases like this, and if you've already added it, it will 'just work'.
Region roles require an accessible name. One obvious way to do this is to use
aria-labeledby pointing at the id of the heading for the region, or if you don't want it to be visible, use aria-label.
If you want to offer the user some way of 'jumping' from the search field to the results (which is what aria-controls is supposed to facilitate), consider adding tabindex=-1 to the results element, as this will allow you to call focus() on that element when the results have arrived. The accessible name will be announced (if you add it), and then the AT user can use table-navigation keyboard shortcuts to browse through the results.
Related
hCaptcha is a fairly popular captcha solution (see a demo.
Their systems works roughly like this:
Websites add a div to a form with a sitekey and also add hCaptcha's JavaScript
hCaptcha adds an iframe and 2 textareas to the form.
When a visitor solves the hCaptcha, the two textareas are filled with a token that is submitted to the site
The site can send the token in a server-to-server call to verify the user passed the captcha
The texteares added in step 2 look like this:
<textarea id="h-captcha-response-0da5o6pd30l5" name="h-captcha-response" style=""></textarea>
<textarea id="g-recaptcha-response-0da5o6pd30l5" name="g-recaptcha-response" style=""></textarea>
According to the pa11y these textareas are not accessible. Screen readers need to know that these form elements are not meant for human input, but are instead just for the functioning of the site. I believe one solution could be to add the HTML attribute aria-hidden="true" to both of these textarea elements.
Is there any solution that consumers of hCaptcha can do? Or what would the best thing for hCaptcha be?
Because these textareas are only used by hCaptcha and not meant to be interacted with by the user, they should be hidden. By using display: none on each textarea, hCaptcha will still be able to include the hidden token in the form submission without causing the accessibility issue.
It's not clear what framework you're using, but the output should look something like this:
<textarea id="h-captcha-response-0da5o6pd30l5" name="h-captcha-response" style="display: none;"></textarea>
<textarea id="g-recaptcha-response-0da5o6pd30l5" name="g-recaptcha-response" style="display: none;"></textarea>
Using the source code below, I need to track text values of clicked links.
How can I track this and whether page load rule or event based rule is beneficiary for it?
How to code this using dtm?
<div class="afgfj">
<section class="asked-questions">
<div class="g-bp-row-gutter p-comp-spacinottom p-rb">
<h2 class="p-heading-02 p-component-title">
Frequently Asked Questions
</h2>
<dl class="p-faq-main p-accordion"
data-ctn="S9031/26">
<dt class="p-top-10 p-faq-chapter p-active">
<span class="p-top-10-global">Top-10 FAQs</span>
<span class="p-top-10-local">Top 10 FAQs</span>
</dt>
<dd class="p-top-10 p-faq-list p-active">
<ul class="p-bullets">
<li class="p-faq-item" data-lang="ENG">
<div class="p-magnific-popup-launcher" data-comp-id="magnificPopupLauncher"
data-type="iframe"
data-title="Frequently asked questions"
data-close-label="Back"
data-href="//www.org.com/cgi-bin/oleeview?view=aa12_view_body.html&dct=QAD&refnr=0073544&slg=ENG&scy=GB&ctn=S9031/26">
How long does it take to get?
</div>
</li>
<li class="p-faq-item" data-lang="ENG">
<div class="p-magnific-popup-launcher" data-comp-id="magnificPopupLauncher"
data-type="iframe"
data-title="Frequently asked questions"
data-close-label="Back"
data-href="//www.org.com/cgi-bin/oleeview?view=aa12_view_body.html&dct=QAD&refnr=0020591&slg=ENG&scy=GB&ctn=S9031/26">
Can I recharge the appliance?
</div>
</li>
This is a perfect time to use an Event Based Rule. You'll also need to create a data element to hold the text value.
The main obstacle that I can see from your code would be identifying the A tag correctly.
First the Data Element: in DTM Rules, within Data Elements click Create New Data Element.
Enter a name, specify the type (CSS Selector seems the most appropriate here) then within the CSS Selector Chain list state how to reach it. My guess is for your code it would be "div.p-magnific-popup-launcher a" but you would need to test this. You can tell by opening a Inspect Element (F12) in Chrome or similar debugging gadget. There's a good blog about doing this from Adobe here.
You should also specify which part of the A tag to save. From your question you I believe you need 'text' which would capture items like "How long does it take to get?"
Under Event Based Rules within DTM click Create New Rule.
When you're happy with the settings on this page click Save Data Element.
Populate your name, and category if applicable. The Event Type should already be set to 'click'.
Within Tag you then have to set how to find the A tag through CSS, similar to above.
That's the basics, but you'll also need to set Criteria (what pages this should fire on). Furthermore, under the Adobe Analytics section you should set whether a pageview is incremented or not, and which eVars, Props and Events are populated as a result of the click. This is also where you can use the value from your Data Element. Under Link Tracking, choose Custom Link. Within Link Name, enter a percent sign (%) and your data elements should appear. Use the name you specified earlier.
Note: you should match up your populated eVars and Events etc. with your settings under Report Suites in the Analytics interface.
I am assuming you are attempting to get the text of an <a> element when it is clicked on.
Such as in the one below, you would want to get "How long does it take to get?":
How long does it take to get?
To do this, create an event based rule with the event type "click", set the element tag to "a". (See image below)
Next you will want to configure the Adobe Analytics section of the rule.
You will set the Tracking to s.tl() ,since you do not want to create a pageview when someone clicks the link (the page they view should already do that).
Then set an eVar and/or Prop to %this.text%. This is DTM notation to grab the text of the element that triggered the rule to fire.
Finally, set an event to trigger on this rule.
See image below for the configuration
This should track when an <a> element is clicked and store the text in an eVar
I am new to Angular & Protractor (and web development for that matter), so I apologize of this is an obvious question.
I am trying to test our angular app with protractor, and it appears that I can locate the first element on the page. But cannot find any of the other elements using (id, name, model, css). I have tried chaining off of the first element, but always get the element not found error on the second element in the chain. I've have triple check the spelling so I am confident everything is correct.
Our page is setup up with multiple sections and when I "view source" I only see the root div.
<head>
</head>
<body>
<div ng-app="app" id="wrap">
<div ui-view></div>
</div>
</body>
</html>
But when I inspect the elements using the developer tools (F12), they exist in the DOM, I just don't know how to get to them.
<input type="text" class="form-control ng-valid ng-dirty ng-valid-parse ng-touched" data-ng-model="vm.searchText" id="searchText" placeholder="(Account # / Name / Nickname / Phone #)">
I tried to access the control listed above using the following:
browser.element(by.id("searchText").sendKeys("test");
browser.element(by.model("vm.searchText").sendKeys("test");
element(by.id("searchText").sendKeys("test");
element(by.model("vm.searchText").sendKeys("test");
I also create a single button and used partialButtonText & buttonText, neither of which worked.
I also tried to add some async functionality with "then" but that didn't work either. How do I access these elements are are not contained in a single html file?
thanks.....
If an element is not visible, I believe protractor isnt able to interact with it. It can't click or get text or anything if it is not visible, that is actually checked before it can perform the action.
What you can do is check the element is present to ensure it is somewhere on the html.
var searchText = $('#searchText');
expect(searchText.isPresent()).toBeTruthy('Search Text element not present');
This will find an element with a css selector of id searchText, and then check if it is present(exists on the html).
If you want to interact with it, remember that protractor looks around like a human would. If a human cant click it, neither can protractor! Make sure it is on the page and visible.
Don't have the reputation points to add this in the comments to user2020347's response so...When you say not in "view source" I assume you're talking about dynamically generated content. Instead of using view source either use chrome or firefox developer tools to make sure you're using the right locators.
For example in chrome's console the following should return a result once the page is loaded:
$$('#searchText')
$$('input[data-ng-model="vm.searchText"]')
It also looks like you're sending keys to the same element.
Since you have an angular app protractor should wait for all elements to load, but just in case you might want to wait for the element to be present and/or visible on the page.
Same happened to me, because Protractor executed on the original (first) tab.
Try to switch between the tabs before accessing the elements:
browser.getAllWindowHandles().then(function (handles) {
browser.driver.switchTo().window(handles[1]);
});
I couldn't find anything in the angularjs docs, nor online, about this specific aspect of form validation. You know when someone writes something in an input field (example: name, phone number, email etc.), and then there is a green checkmark that appears? Or an X that appears implying it's wrong, incomplete etc.
So, I have those images in my folder and ready for use in either situation. Problem is, I can't find the documentation to properly achieve what I would like to achieve. I am thinking that angularjs would be the solution to use, as the rest of my code in is angular.
Since this is angularjs, the only post and documentation that presented a viable option (which does not work for a few reasons) are the following options:
How to put an image in div with CSS?
https://docs.angularjs.org/tutorial/step_09
I was thinking of using CSS to trick the browser into making the one or the other image appear as it validates. I thought it might force the image in my other div to appear, but to no avail.
For example, in this CSS, I tried this:
.ng-valid.ng-dirty .div.test{
border-color: green;
content:url(http://example.com/image);
}
Using this in my HTML:
<div class="test">
<label style="float:left">by:</label>
<input class="form-control controltwo" required ng-model="reviewCtrl.review.author" name="email" id="email" type="email" style="width:350px;" placeholder="Email Address"/>
</div>
As I said before, I am trying to achieve something using angularjs. As CSS can be used for styling, it cannot be tricked into being a styling option and a complex validator. I've tried a few tricks as show on the links, but they don't work. As for the second link, it just isn't made for this purpose, and considering they are made only for filters and images, the docs for the filters don't help a bit.
A simple way of achieving what you want is to look in to the $valid or $invalid properties of your form control.
For example, to show a small message when the email is invalid, you would put this element in your markup.
<div ng-show='reviewForm.email.$dirty && reviewForm.email.$invalid'>Invalid Email</div>
Where reviewForm is the name of your form, and email is the name of your input control.
Here is a plunkr demonstrating this: http://plnkr.co/edit/tUuToy99xjfMhbyMd3eV
You can replace the element with whatever else you want
You can do this with ng-show, ng-src and ng-model depending on what you're validating.
https://docs.angularjs.org/api/ng/directive/ngModel
https://docs.angularjs.org/api/ng/directive/ngShow
https://docs.angularjs.org/api/ng/directive/ngSrc
The example under ng-model:text shows pretty much what you want. If you're not using forms, you should be able to use ng-change to fire off a check and change the image to the appropriate one.
https://docs.angularjs.org/api/ng/input/input%5Btext%5D
If ng-show watches the $valid attribute of the field in question you can hide the check mark when validation is false, and show it when true. You can flip the logic if you want an X.
I need to display part of the exposed form in my page's sidebar, and the rest of the form and content in the $content area. There's really no good way that I can find to do this. I sort of got it to show up in a way by making a "block" view with "exposed form" set and then trying to only show the part that i needed through .tpl files. The problem is that then, when the submit button is clicked (the submit button is in the $content area), then the filters that are in the sidebar are not taken into account.
Some lateral thinking... Why not explore CSS-only options? You can place that form element playing with position:absolute ? Or (considering is a right-sidebar) float:right and then some negative right margin to push it to the sidebar? If you are using 960 grid system, play with pull and push classes.
First I am going to answer your question, then I will explain why you are asking the wrong question:
If you build the form outside of the formapi, you might have some luck. This will get upgly and will require you to take a lot of extra care about attack-vectors such as mass-assignment.
views_some_view.tpl.php:
<form name="input" action="/link/to/view" method="get">
Country: <input type="text" name="country" />
my_custom_exposed_view.module:hook_block()
City:
That would make a form, which in most situations will start with <form>, have some input fields, then have a lot of random HTML, then some more input fields and then the closing .
As you may know, a <input type="submit" value="Submit" /> will only post everything of the form tags it is enclosed in. The submit button in the following HTML:
<form name="input_1" action="/link/to/view" method="get">
Country: <input type="text" name="country" />
</form>
<form name="input_2" action="/link/to/view" method="get">
City: <input type="text" name="city" />
<input type="submit" value="Submit" />
</form>
will only send the City. These are not the droids you are looking for.
It will need to be one, big form, but since everything between form and /form is very dynamic, and contains a large quantity of HTML, including potential other forms, this is really not what you want. Moreover: a blocks appearance (shown/not-shown) is controlled completely independent of the content. You will need a lot of sturdy code to ensure the block a) never shows up when the starting form tag is not present, and b) the block will guaranteed to be shown when that opening form tag is present. Else you have not just invalid HTML, but broken HTML that will truly render your page unusable in most cases.
You simply don't want a part of the form in a block and the other part in the content.
However, you want it visualised as if one part is in the body, the rest in a sidebar.
The good news, is that with HTML presentation structure are independant. That is where your solution lies.
Give your form-fields good ids and classes. You could use a hook_form_alter to change existing forms, but you probably simply just want to create the HTML for that entire form yourself. The theme layer allows that.
Use CSS to pick out either single form-fields by ID and position:absolute them into the correct place. Or pick out classes of fields by CLASS and position:relative them into the correct place.
Make a simple identification-routine that allows adding a class to the body-tag. (see below).
Add some CSS to shift the sidebar lower, making space for the form-fields to be moved in, when that class is in the body-tag.
<body class="<?php print $splitform ?>">
function my_themename_preprocess_page() {
if ($GET['q'] == 'path/to/view') {
$vars['spliform'] = "splitform"
}
}
From the above explanation I am assuming that you are printing same form in block and in content area and you are hiding some part of form in page.tpl , if this is true then you can use hook_form_alter() in your custom module then
Store the value of the form element(present in block) in global variable.
Now use that global variable and set form element(present in content area, this form element is not visible to user).
Provide more information if you implemented other way.
Regards,
Chintan.
There is a related issue here:
https://drupal.stackexchange.com/questions/3827/multiple-copies-of-views-filter-form-exposed-filters
which describes how to duplicate your filters. However it seems like an ugly hack.
A bit cleaner seems this solution mentioned in #6:
http://drupal.org/node/641838#comment-3247748
Haven't tested it out, but it looks good.
It will still give you some overhead (duplicate views) but it might be the easiest way doing this using views.
On the other hand you might write a module and build your own custom filter block which hooks into your view. Here is a blog post about this:
http://www.hashbangcode.com/blog/creating-custom-views-filters-exposed-form-element-drupal-6-561.html
If you use something like context you could get the exposed filters block to display twice in the same page. You could then use CSS to hide the fields you don't want to do display in each form.
The fundamental problem you're having is that to have the two forms in different places, they'll each have their own form element - when a submit is triggered, only the form fields within the same form element are sent. You need to move them into one form, or rely on JavaScript to gather the fields from both forms and construct the post.
You could create the block as an empty div and have javascript from the main page populate it with the secondary filter form and whatever else you need in there. Again, you could use javascript to copy the form values from the block form to hidden fields in the main form on submit. That gives you all the control you need from one place (the node output). Only caveat is that it relies a lot more on javascript to join it all together.