How can I make Twitter Bootstrap tooltips accessible? - javascript

As you may know, the Twitter bootstrap tooltips are not accessible (I.E. they are not being read by screen readers). To make that happen, the following things should be done:
Upon calling the tooltip() function, the generated text element (the one that contains the text of the tooltip) should get a new attribute added to it: aria-hidden="true".
The original element (the one tooltip() has been called on) should get an attribute added to it: aria-describedby="#<tooltip-id>", where tooltip-id refers to the id of the new element that was just created above.
Since the way the Javascript currently works is selecting all the elements with the .tooltip class and applying the tooltip() function to it, I'm wondering how I can do this without modifying the source code of the tooltip() function.
Here is an example of the code for a button:
<span role="button" rel="tooltip" title="Add Youtube Video" class="fancyPostButton span1" tabindex="0" style="-webkit-user-select: none;padding-top: 10px">
<div id="fancyPostVideoPicker" class="fancyPostAttachment videoAttachment glyphicons film centerMe">
<i></i>
</div>
</span>

From the looks of the main example in the Bootstrap docs they are keyboard accessible (i.e. show to keyboard-only users) when used on natively-focusable elements, and they use text-content for the buttons.
So the default Bootstrap examples aren't too bad, if the tooltip text were needed to understand the button (like your example) that is when they create an accessibility issue.
So the main thing is that the button has no content, so a screen reader won't know what it is to start with. (NB: Some screen readers might fall back to the title if it were a native link or button, but you shouldn't rely on that.)
To do a button that shows a font-icon, you need content and the icon, so two elements:
<a href="target.html">
<span class="icon-home" aria-hidden="true"></span>
<span class="alt">Add YouTube Video</span>
</a>
Then use CSS to hide the text offscreen. The ARIA-hidden means that the font character won't be read out.
I would also recommend actually using a button or link, rather than span or div, as then you don't need to use javascript to simulate onclick etc. In this case, you've also got an inline element (span) wrapped around a block element (div) which isn't valid HTML either.
If you use the technique above screen reader users will hear the content of the item anyway, I don't think there is any other text to read out?
If you can't add the hidden text in the source, you could loop through the tooltip elements adding it dynamically.

Related

close button in bootstrap tabs - without putting <button> inside <a>?

This question Close Button for Twitter Bootstrap3 Tabs shows how to create a Bootstrap3 tab with a close button in it:
<li class="active">Tab 1 <button><i class="fa fa-times"></i></button>
This works for me (I'm using glyphicons instead of font awesome) but then i read here Can I nest a <button> element inside an <a> using HTML5? that it's not acceptable to have a <button> inside of an <a> tag.
What is the right way to add a button to a bootstrap tab if it's not allowed to be inside the <a> element?
This JSFiddle (not mine) http://jsfiddle.net/vinodlouis/pb6EM/1/ shows the functionality I am going for with the little x button on the tabs.
If you want to insert a button in an anchor, the answer is you cannot (or rather you should not). The following two examples show how to get an html button to behave as a link; either by styling an anchor to look like a button (example 1), or using the onclick event of a button to trigger a JavaScript redirection as an anchor would (example 2).
<!DOCTYPE html>
<html>
<head>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
</head>
<body>
<!-- Styling the anchor as a button -->
An anchor that looks like a button<br/>
<!-- Letting JavaScript do the redirection -->
<button type="button" class="btn btn-default" onclick="javascript:location.href='#'">A button with onclick JS redirection</button>
</body>
</html>
Edit: Your latest edit and comment make it clearer what you are after, but I don't understand why you would phrase it as "having a button in an anchor". Bootstrap tabs are neither anchors nor buttons, so I would say the OP is really not well formulated. And being able to close a tab does not require either.
Specifically to answer you question now, you should put the cross-icon into a span with left padding and use the onclick event to trigger the tab's deletion with JavaScript.
What is the right way to add a button to a bootstrap tab if it's not allowed to be inside the element?
Simply substitute it for an element that is allowed at that position …?
From (taken from the jsfiddle example you linked to)
<li><button class="close closeTab" type="button">×</button>Sent
to
<li><span class="close closeTab">×</span>Sent
and done.
That might still give you unexpected results when the user interacts with it, of course – after all, when they click that “button”, they are clicking the link as well. So whatever scripting gets attached to the close button, will have to take that into account, and stop event propagation or something like that.
The real proper way to do this would of course be to not nest those elements, that are supposed to have completely different functionality, in the first place, but make them separate, independent elements. Overlying one on top of the other is a matter of formatting.

How to remove/hide bootstrap tooltip when element is removed from the DOM

I'm currently working on a project in ReactJS. Some of my components are not rendered all the time but change dynamically based on certain conditions. When these components have a tool tip attached to them, I'm noticing that if the tooltip was active when the element was hidden, the tooltip does not go away. I'm looking for a way to remove or at least hide this tooltip when the element is not being rendered.
This is how I'm activating the tooltips using jQuery:
$(document).ready(function() {
$("body").tooltip({ selector: '[data-toggle=tooltip]', placement: 'bottom' })
})
This is how I'm using it within the html (or jsx):
<a className="icon-btn" onClick={ () => {
//on Click I remove this parent element and show something else
}}>
<i className="fa fa-lg fa-pencil-square" title="Edit" data-toggle="tooltip"></i>
</a>
Note I have not been able to select all elements by tooltip using:
$('[data-toggle=tooltip]').tooltip()
Apparently that is because I am adding elements dynamically? At least that is what my research so far shows
$('.tooltip').tooltip('hide');
I was having a similar problem and I know that the question is already a bit old but I post my solution, maybe it helps someone in the future... (It's an Angular-TypeScript-JQuery mixture but the principle is the same.)
In the component:
/* Toggles the Bootstrap 4 tooltip of an HTML element. */
private tooltip(elem: HTMLElement, action: string): void {
(<any>$(elem)).tooltip("dispose");
(<any>$(elem)).tooltip({ container: "body" });
(<any>$(elem)).tooltip(action);
(<any>$(elem)).tooltip("update");
}
In the view:
<someElement data-toggle="tooltip" title="..."
(mouseenter)="tooltip($event.target,'show')"
(mouseleave)="tooltip($event.target,'hide')">
</someElement>
So basically instead of using a global $('[data-toggle=tooltip]').tooltip() for activating all tooltips at once – which wouldn't work anyway if you have tooltips not being part of the DOM at the time this code is called because the page is generated dynamically by some JS framework for example – you only attach the tooltips to the elements when the cursor enters them, and destroy them immediately when it leaves.
(The { container: "body" } and "update" options are only to prevent positioning issues like this for more complex layouts.)
Though this is an older one, this may help someone in future...I encountered this issue in a Vue app and setting the tooltip to hover only did not solve the issue for me (I also wanted it to appear on focus for accessibility reasons). Ultimately, setting data-container to a parent element worked for me which then creates the tooltip inside this element (in my case, the entire parent element was being removed so the tooltip is then removed with it). Note, you may need to set data-boundary to "window" as well if you run into tooltip placement issues - this will ensure it will overflow the container. For example:
<button data-toggle="tooltip" data-placement="right" data-container=".container" data-boundary="window"></button>
None of the accepted answers seemed to resolve my issue, and would just leave the tooltip floating mid-page, and then upon scrolling, it would move to the top left of the page due to the absolute positioning. (I'm using Vue, and the element already had an #click event on it, so I added a parent wrapping div and applied an inline event there)
<div onclick="removeTT()">
<div data-toggle="tooltip" data-placement="top" title="Tooltip on top"></div>
</div>
<script>
function removeTT() {
$('.tooltip').tooltip('hide');
}
</script>

addThis button not clickable via jQuery

I've tried looking at the documentation for addThis and it seems like it's being updated or something because all the links these help posts link to don't even mention the API bits they describe.
Anyway,
I just need to be able to programmatically click an addThis button. However, I can't seem to do it via console before I implement it in my code.
I read that this has something to do with how the addThis listeners are added only when the document is done loading. This doesn't make sense to me because even if I manually try to trigger a click in console, it still does nothing but return the html of the link I'm trying to trigger. For example:
`$('.at-svc-facebook').click();`
OR `$('.at-svc-facebook').trigger('click');`
OR `$('.at-share-btn.at-svc-facebook').click();`
I mean, by the time I open console the dom is ready. So then what else might be preventing me from clicking these buttons via jQuery?
I've tried adding a listener to an element myself, and then clicking it programmatically, and it works. So something is different about the way addThis listens for a click. I may update this question with something I find after inspecting their js.
===================
This is what is in the DOM which addThis populates and listens to:
<div class="addthis_sharing_toolbox"></div>
This is what ^ that code is turned in to from addThis:
<div class="addthis_sharing_toolbox" data-url="http://localhost:8001/halloween/" data-title="33 Halloween Costume Ideas">
<div id="atstbx" class="at-share-tbx-element addthis_32x32_style addthis-smartlayers addthis-animated at4-show">
<a class="at-share-btn at-svc-facebook">
<span class="at4-icon aticon-facebook" title="Facebook"></span>
</a>
<a class="at-share-btn at-svc-twitter">
<span class="at4-icon aticon-twitter" title="Twitter"></span>
</a>
<a class="at-share-btn at-svc-google_plusone_share">
<span class="at4-icon aticon-google_plusone_share" title="Google+"></span>
</a>
</div>
</div>
Now, you can see the jQuery I'm using to click the buttons and the code it's trying to click.
The issue is that addThis was putting a second link with the exact same class in the DOM for some reason. It generates some HTML and appends it to body.
So what I needed to do to select the button and trigger a click was to specify the 2nd element in the array of elements and call click on that one. Like so:
$('.at-svc-facebook')[1].click();
Now, the next problem I face is chrome block a programatic popup, but that's beyond the scope of this question. :(
change the
var class_name = $(this)[0].className;
as below,
var class_name = $(this)[0].attr('class');
and it'll work. :)

Current menu item

I've been trying every single tutorial I found online and none seems to work for me.
I've got these buttons:
<a href='faq.php'><div class='button'>
<div class='button_top'>
</div>
<div class='button_bot'>
FAQ
</div></a>
http://jsfiddle.net/6G8bk/
and I'd like that the top of the button would stay highlighted if the page url is same as href of the button.
Ty for any answers in advance!
Here's the fixed jsfiddle with jquery I tried but still won't work: http://jsfiddle.net/6G8bk/4/
A few things:
In your jQuery, you're trying to select all <a> elements that have a parent class of button, and according to your HTML you do not have (the button class is a child of the <a> element).
The page's URL won't work in JSFiddle because it will get the JSFiddle link, which will be different from the one on your website.
Since you want button_top to be visible on hover, you'll need to use JavaScript. As fas as I know, you can't manipulate another element on hover with pure CSS.
Here is a working Fiddle of what I think you want. I've left comments in the code that might help you.
http://jsfiddle.net/6G8bk/6/
You can retrieve the current url page by using $_SERVER["SCRIPT_NAME"] and comparing it to each element of the menu.
If it match, you put another class in the menu element with CSS rules to have the layout you want.

Expand/Collapse Text

The code below works fine with ONE Reveal/Hide Text process
<div class="reveal">Click Here to READ MORE...</div>
<div style="display:none;">
<div class="collapse" style="display:none;">Collapse Text</div>
However if this code is duplicated multiple times, the Collapse Text shows up and doesn't disappear and in fact conflicts with the Expand to reveal even more text instead of collapsing as it should.
In this http://jsfiddle.net/syEM3/4/ click on any of the Click Here to READ MORE...
Notice how the Collapse Text shows up at the bottom of the paragraphs and doesn't disappear. Click on the Collapse and it reveal more text.
How do I prevent this and getting to work as it should?
The two slideDown function calls are not specific to the .reveal and/or .collapse that you are currently doing. i.e.
$(".collapse").slideDown(100);
will find all the elements with the class .collapse on the page, and slide them down. irrespective of what element you just clicked.
I would change the slideDown call to be relavant to the element you just clicked i.e. something like this
$('.reveal').click(function() {
$(this).slideUp(100);
$(this).next().slideToggle();
$(this).next().next(".collapse").slideToggle(100);
});
in your code
$('.reveal').click(function() {
$(this).slideUp(100);
$(this).next().slideToggle();
$(".collapse").slideDown(100);
});
$('.collapse').click(function() {
$(this).slideUp(100);
$(this).prev().slideToggle();
$(".reveal").slideDown(100);
});
this two rows doesn’t do what you want as they act on all elements of the specified class
$(".reveal").slideDown(100);
$(".collapse").slideDown(100);
When you do $(".collapse").slideDown(100);, jQuery runs slideDown on everything with the .collapse class, not just the one that's related to your current this. To fix this, refer to the collapse based on its location to $(this).
Do do this, use something like $(this).siblings(".collapse").slideDown(100);
Note that this particular selector will only work if you enclose each text block in its own div. With each text element in its own div, like you have it now, .siblings(".collapse"), which selects all the siblings of $(this) with the collapse class, will still select both of the collapse elements.
Okay, I think you should take a different approach to your problem.
See, jQuery basically has two purposes:
Selecting one or more DOM elements from your HTML page
manipulate the selected elements in some way
This can be repeated multiple times, since jQuery functions are chainable (this means you can call function after function after function...).
If I understood your problem correctly, you are trying to build a list of blog posts and only display teasers of them.
After the user clicks the "read more" button, the complete article gets expanded.
Keep in mind: jQuery selects your elements very much like CSS would do. This makes it extremely easy to
come up with a query for certain elements, but you need to structure your HTML in a good way, like
you would do for formatting reasons.
So I suggest you should use this basic markup for each of your articles (heads up, HTML5 at work!):
<article class="article">
<section class="teaser">
Hey, I am a incredible teaser text! I just introduce you to the article.
</section>
<section class="full">
I am the articles body text. You should not see me initially.
</section>
</article>
You can replace the article and section elements with div elements if you like to.
And here is the CSS for this markup:
/* In case you want to display multiple articles underneath, separate them a bit */
.article{
margin-bottom: 50px;
}
/* we want the teaser to stand out a bit, so we format it bold */
.teaser{
font-weight: bold;
}
/* The article body should be a bit separated from the teaser */
.full{
padding-top: 10px;
}
/* This class is used to hide elements */
.hidden{
display: none;
}
The way we created the markup and CSS allows us to put multiple articles underneath.
Okay, you may have noticed: I completely omitted any "read more" or "collapse" buttons. This is done by intention.
If somebody visits the blog site with javascript disabled (maybe a search engine, or a old mobile which doesn't support JS or whatever),
the logic would be broken. Also, many text-snippets like "read more" and "collapse" are not relevant if they don't actually do anything and are not part of the article.
Initially, no article body is hidden, since we didn't apply the hidden css class anywhere. If we would
have embedded it in the HTML and someone really has no JavaScript, he would be unable to read anything.
Adding some jQuery magic
At the bottom of the page, we are embedding the jQuery library from the google CDN.
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
This is a best practice and will normally speed up your page loading time. Since MANY websites are embedding
jQuery through this URL, chances are high that its already in the visitors browser cache and doesn't have
to be downloaded another time.
Notice that the http: at the beginning of the URL is omitted. This causes browsers to use the pages current protocol,
may it be http or https. If you would try and embed the jQuery lib via http protocol on a https website, some browsers will refuse to download the file from a unsecure connection.
After you included jQuery into the page, we are going to add our logic into a script tag. Normally we would
save the logic into a separate file (again caching and what not all), but this time a script block will do fine.
Finally some JavaScript
At first, we want to hide all elements with the css-class full, since only teasers should remain displayed. This is very easy with jQuery:
$('.full').hide();
The beginning of the script $('.full') tells jQuery: I need all elements with the CSS-class full. Then we call a function on that result, namingly hide() which purpose should be clear.
Okay, in the next step, we want to add some "read more" buttons, next to every teaser. Thats an easy task, too:
$('.teaser').after('<button class="more">Read more</button>');
We now select every element with the css-class teaser and append some HTML code after() each element - a button with the css-class more.
In the next step, we tell jQuery to observe clicks on every one of this freshly created buttons. When a user has clicked, we want to expand the next element with the css-class full after the clicked button.
$('.more').on('click', function(){
//"this" is a reference to the button element!
$(this).slideUp().next('.full').slideDown();
});
Phew, what did we do here?
First, we told jQuery that we wanted to manipulate this, which is a reference to the clicked button. Then we told
jQuery to hide that button (since its not needed anymore) slowly with slideUp().
We immediately continued telling jQuery what to do: Now take the next() element (with the css-class full) and make it visible by sliding it down with slideDown().
Thats the power of jQuerys chaining!
Hiding again
But wait, you wanted to be able to collapse the articles again! So we need a "collapse" button, too and
some more JavaScript:
$('.full').append('<button class="collapse">Collapse text</button>');
Note: we didn't use the after() function to add this button, but the append() function to place the button
INSIDE every element with the css-class full, rather than next to it. This is because we want the
collapse buttons to be hidden with the full texts, too.
Now we need to have some action when the user clicks one of those buttons, too:
$('.collapse').on('click', function(){
$(this).parent().slideUp().prev('.more').slideDown();
});
Now, this was easy: We start with the button element, move the focus to its parent() (which is the element that contains the full text) and tell jQuery to hide that element by sliding it up with slideUp().
Then we move the focus from the full-text container to its previous element with the css-class more, which is its expanding button that has been hidden when expanding the text. We slowly show that button again by calling slideDown().
Thats it :)
I've uploaded my example on jsBin.

Categories