Accessible popovers for additional help information - javascript

tl;dr: How would I implement an accessible popover? (I'm using bootstrap at the moment, but open to any ideas)
One of the biggest accessibility problems I face is the use of popovers when providing extra help information.
The way I understand it, popovers (and I guess more broadly, modals) are used for extra information when a tooltip cannot suffice. Popovers require the user to "open/click/activate" a help button for a new DOM element to be appended (or otherwise displayed), with the help information. While tooltips passively shows/speaks information whenever a user focuses the element. The tooltip widget itself is still being considered by WAI-ARIA
Say I have a text-input field with a label. I want to attach supplementary content to that input, in the form of a clickable/actionable button.
Initially I had the popover button in the label, before finding out the hard way that label's can't have block-level child elements.
I'm aware this probably isn't the best way of implementing an accessible popover. Here is my help button:
<div class="facs-ctl-help">
<a role="button" aria-label="Show help for Template Id 2,003" tabindex="0" data-toggle="popover" title="">
<span class="fa fa-info btn-floating waves-effect facs-help-icon"></span>
</a>
</div>
and here is my popover:
<div class="popover tooltip-help" role="tooltip">
<div class="arrow"></div>
<h3 tabindex="-1">
<i class="fa fa-info btn-floating waves-effect facs-help-icon"></i>Help
<span class="offscreen"> for Template Id 2,003</span>
<span class="offscreen"> popup</span>
</h3>
<div class="popover-body"></div>
<input type="button" class="btn pull-right tooltip-hide-btn" value="hide" aria-label="Hide help for Template Id 2,003">
<div style="width: 100%; clear:both;"></div>
</div>
What should I be using/doing? Would this be a good use case for live-regions? If so, any example code?

It doesn't necessarily have to be a live region. It feels more like a modal dialog, especially if you have interactive elements in it, such as the hide button.
There's a working example of an accessible modal dialog on https://github.com/gdkraus/accessible-modal-dialog

Related

Event listener disappears inside ng-repeat

My HTML form has many questions I create using ng-repeat.
I want to create a pop-over for some questions.
When I put the button that triggers the pop-over outside the ng-repeat, it works. Inside the ng-repeat it does not.
This works (button before ng-repeat):
<div class="row">
<div class="col-md-12">
<button title="" data-toggle="popover" data-placement="top" data-trigger="hover" data-title="Popover on hover" data-content="And here's some amazing content. It's very engaging. Right?" class="btn btn-primary btn-wide">
hover
</button>
<div ng-repeat="q in config.questionnaire.questions">
Producing this:
This does not work (button after ng-repeat):
<div class="row">
<div class="col-md-12">
<div ng-repeat="q in config.questionnaire.questions">
<button title="" data-toggle="popover" data-placement="top" data-trigger="hover" data-title="Popover on hover" data-content="And here's some amazing content. It's very engaging. Right?" class="btn btn-primary btn-wide">
hover
</button>
Inspecting the button element on google chrome, I found that some event listeners were removed:
These are the event listeners for the button before the ng-repeat:
These are the event listeners for the button after the ng-repeat:
As you can see, mouseover and mouse out listeners disappeared.
Why?
What can I do?
If you are using Bootstrap with your Angular project, think of using Angular UI library.
https://angular-ui.github.io/bootstrap/#/popover
It provides many directives which make it easier to connect Bootstrap with Angular. I tested the uib-popover directive with ng-repeat and it works properly this way. The code is pretty simple:
//...
<div ng-repeat="t in main.testArray">
<button uib-popover="My popover content" popover-title="Popover title" popover-trigger="mouseenter">{{t.text}}</button>
</div>
//...
I will try to investigate your problem further, but the library seems to be an easy workaround.
Edit
I think I also managed to figure out a solution without using external library. What I did was creating a directive which initializes Boostrap popover on specified element. This way you make sure that the popover function will be fired when elements are rendered. I guess this was the reason why it didn't work with your method. You probably invoked the popover function before elements were really rendered as ng-repeat does this with a delay.
And here is the code:
angular.module('moduleName')
.directive('bootstrapPopover', function() {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.popover();
}
};
});
And then use directive on element:
<div ng-repeat="t in main.test">
<button data-toggle="popover" data-placement="top" data-trigger="hover" data-title="Test" data-content="Test"
class="btn btn-primary btn-wide" bootstrap-popover>hover {{t.text}</button>
</div>
However, I would still recommend trying the Angular UI out. I used it in the last Angular project and it was really helpful.

making accordion panel open when clicking on header rather than just title

I found this stackoverflow questions which looks to be the same problem I am having. That is the default behavior is for angular ui-bootstrap accordion to only open when you click on the panel title and not anywhere else on the panel.
ng-click on accordion panel header
The solutions presented seems to get out of sync when one time clicking on the panel and then clicking on the title. Sometimes you have to click twice to get it back in sync. I noticed this snippet on the documentation, To use clickable elements within the accordion, you have override the accordion-group template to use div elements instead of anchor elements, and add cursor: pointer in your CSS.
Can someone provide and example of using div tags instead of anchor elements?
You have to customise the template for accordion-group to use clickable links otherwise it will trigger unexpected routing. You can modify the template as below:
<script id="uib/template/accordion/accordion-group.html" type="text/ng-template">
<div role="tab" id="{{::headingId}}" aria-selected="{{isOpen}}" class="" ng-keypress="toggleOpen($event)">
//this is previously <a role="tab"> - replace it with div
<div role="button" style="border-top:1px solid #e6e6e6" data-toggle="collapse" href aria-expanded="{{isOpen}}" aria-controls="{{::panelId}}"
tabindex="0" ng-click="toggleOpen()" uib-accordion-transclude="heading" ng-disabled="isDisabled"
uib-tabindex-toggle><span uib-accordion-header ng-class="{'text-muted': isDisabled}">{{heading}}</span>
</div>
</div>
<div id="{{::panelId}}" aria-labelledby="{{::headingId}}" aria-hidden="{{!isOpen}}" role="tabpanel"
class="panel-collapse collapse" uib-collapse="!isOpen">
<div class="" ng-transclude></div>
</div>
</script>
You can put this template in your view where you are using accordion. This will override your default accordion-group template that comes from angular ui bootstrap tpls library.

Summernote error on Meteor and Semantic-ui

I used summernote package: summernote:summernote for my website and everything work well except the feature to insert image, videos and link won't work.
Example:
Click insert link button (image and videos are the same)
A popup appear to set the link.
Click anywhere on that popup, it disappeared.
Here are my code:
post_edit.html
<template name="postEdit">
<div class="ui segment">
<form class="ui form">
<h1 class="ui dividing header">Edit post</h1>
<div class="field">
<label>Title</label>
<input type="text" id="title" name="title" value="{{title}}">
</div>
<label>Content</label>
<div class="field" id="content" name="content">
{{{content}}}
</div>
<button type="submit" class="ui orange button"><i class="edit icon"></i> Edit</button>
<a class="negative ui button delete"><i class="remove icon"></i> Delete</a>
<a class="ui button" href="{{pathFor 'postPage'}}"><i class="arrow left icon"></i> Back</a>
</form>
</div>
post_edit.js
Template.postEdit.onRendered(function(){
$(document).ready(function() {
$('#content').summernote({
height: 400,
maxHeight:800,
minHeight:250,
});
});
});
I ran into this issue myself when adding autoform-summernote to my project that already uses semantic-ui. The problem is that there's a conflict between Bootstrap's and Semantic UI's $.modal() method. See these links for code references:
Summernote modal init
Bootstrap's modal definition
Semantic UI's modal definition
Summernote expects the modal method to be Bootstrap's, but instead calls Semantic UI's modal method. Because of the differences between the implementations of the modal methods, the modal closes right away when you click anywhere in the window.
Without some low-level hacks, these two packages will conflict since Semantic UI's is available globally in your project on any $('object'). If you're not using Semantic UI's modal method anywhere else in your site, you could disable it, which will fix it in this case. However, that's not a solution that works for me. Instead, I'm looking into a solution to remove summernote, or at least its dependency on Boostrap.
Edit 1/13/2016
I ended up replacing summernote with a different editor, https://atmospherejs.com/gildaspk/autoform-medium. It gives me the functionality I need and doesn't have conflicting dependencies.

Using Bootstrap, I can only get the text to act like a link, not the whole button?

I'm just learning how to use html and css and my teacher has asked us to use Bootstrap. It's really cool, obviously, but when I try to make a button, only the text within the button actually acts like a link as opposed to the whole rectangular button. I'm not sure if I need more than just the minified bootstrap javascript file to make them work or what.
Here's my html, and I also added the line "$('.nav-tabs').button()" to my head as well as the javascript file from bootstrap. Any advice? I know my html is probably pretty janky, my teacher isn't the best at explaining things so I've just been fiddling with things until they seem to work.
<div class="btn-toolbar">
<div class="row-fluid">
<div class="span2 offset2">
<div class="btn btn-primary">
<a href="http://students.cec.wustl.edu/~amd4/Portfolio/portfolio%20-%20profile%20-%20final.html">
Profile
</a>
</div>
</div>
<div class="span2 offset.5">
<div class="btn">
<a href="http://students.cec.wustl.edu/~amd4/Portfolio/portfolio%20-%20writing%20-%20final.html">
Writing
</a>
</div>
</div>
<div class="span2 offset.5">
<div class="btn">
<a href="http://students.cec.wustl.edu/~amd4/Portfolio/portfolio%20-%20music%20-%20final.html">
Music
</a>
</div>
</div>
<div class="span2 offset.5">
<div class="btn">
<a href="http://students.cec.wustl.edu/~amd4/Portfolio/portfolio%20-%20photos%20-%20final.html">
Photography
</a>
</div>
</div>
</div>
remove the class="btn btn-primary" from the div tag, put it on the a tag
see http://twitter.github.com/bootstrap/base-css.html#buttons
...typically you'll want to apply these to only <a> and <button> elements for the best rendering.
Looks like you are not adding the class on the a tag.
You need to use something like this New button This should give you a button with the text New button.
You use Big Button
For a large blue button.

AngularJS: Data-bound modal - save changes only when "Save" is clicked, or forget changes if "Cancel" is clicked

I have a list of items, and upon clicking on one of the items, a modal dialog is displayed for the user to make some changes and click either "Close" or "Save changes".
The problem is that say that user makes some changes and clicks on "Close", the changes would have been reflected in the model the view is bound to, since data-binding is instant.
My question is then, how do I either defer the updates and only perform binding when "Save Changes" is clicked, or somehow forget the changes if "Cancel" is clicked.
The code for my modal dialog is like so:
<div ui-modal class="fade static" ng-model="modalShown" id="myModal" data-backdrop="static">
<div class="modal-header">
<button type="button" class="close" ng-click="closeModal()" aria-hidden="true">×</button>
<h3>{{selectedClientFeature.feature.type}}</h3>
</div>
<div class="modal-body">
<ul class="unstyled columnlist">
<li ng-repeat="country in countriesForEdit">
<input type="checkbox" ng-model="country.selected"> {{country.name}}
</li>
</ul>
</div>
<div class="modal-footer">
<a ng-click="closeModal()" class="btn">Close</a>
<a ng-click="saveChanges()" class="btn btn-primary">Save changes</a>
</div>
</div>
Thanks,
Shaun
The angularjs doc use to have an example of just this situation. What you would need is to clone your model (see angular.copy), prior to showing your edit modal, and when a user clicks on closeModal() you would reassign your model to the cloned value. IMHO, i would rename your 'Close' button to 'Cancel' and put it to the right of 'Save Changes', this is more explicit and seems to be the way many sites work.
Hope this helps
--dan
To automate manual cloning/updating model I came up with lazy-model directive.
See https://stackoverflow.com/a/20643001/740245

Categories