I would like display combobox on IE 11 by referring this article https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.0pattern/combobox-autocomplete-none.html
but for some reason i can not display the listbox items on IE but for other browsers it works fine. Any idea?
If I try to remove this line
this.domNode = domNode;
it shows me the list but not clickable.
javascript class
/*
* This content is licensed according to the W3C Software License at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
*/
var Option = function (domNode, listboxObj) {
this.domNode = domNode;
this.listbox = listboxObj;
this.textContent = domNode.textContent;
this.textComparison = domNode.textContent.toLowerCase();
};
Option.prototype.init = function () {
if (!this.domNode.getAttribute('role')) {
this.domNode.setAttribute('role', 'option');
}
this.domNode.addEventListener('click', this.handleClick.bind(this));
this.domNode.addEventListener('mouseover', this.handleMouseover.bind(this));
this.domNode.addEventListener('mouseout', this.handleMouseout.bind(this));
};
/* EVENT HANDLERS */
Option.prototype.handleClick = function (event) {
this.listbox.setOption(this);
this.listbox.close(true);
};
Option.prototype.handleMouseover = function (event) {
this.listbox.hasHover = true;
this.listbox.open();
};
Option.prototype.handleMouseout = function (event) {
this.listbox.hasHover = false;
setTimeout(this.listbox.close.bind(this.listbox, false), 300);
};
HTML
<section>
<h2 id="ex_label">Example</h2>
<div role="separator" id="ex_start_sep" aria-labelledby="ex_start_sep ex_label" aria-label="Start of"></div>
<div id="ex1">
<div class="combobox-list">
<label for="cb1-input">Search</label>
<div class="group">
<input id="cb1-input" class="cb_edit" type="text"
role="combobox"
aria-autocomplete="none"
aria-expanded="false"
aria-haspopup="true"
aria-owns="cb1-listbox"
readonly
/>
<button id="cb1-button" tabindex="-1" aria-label="Open">
▽
</button>
</div>
<ul id="cb1-listbox" role="listbox" aria-label="Previous Searches">
<li id="lb1-01" role="option">weather</li>
<li id="lb1-02" role="option">salsa recipes</li>
<li id="lb1-03" role="option">cheap flights to NY</li>
<li id="lb1-04" role="option">dictionary</li>
<li id="lb1-05" role="option">baseball scores</li>
<li id="lb1-06" role="option">hotels in NY</li>
<li id="lb1-07" role="option">mortgage calculator</li>
<li id="lb1-08" role="option">restaurants near me</li>
<li id="lb1-09" role="option">free games</li>
<li id="lb1-10" role="option">gas prices</li>
<li id="lb1-11" role="option">classical music</li>
</ul>
</div>
</div>
<div role="separator" id="ex_end_sep" aria-labelledby="ex_end_sep ex_label" aria-label="End of"></div>
</section>
Related
I have the JS code below that I use with the form after it. However, it only calls the first dropdown and ignores the rest. The dropdown are generated by Hugo (SSG) and I all share the same ID/class in the menu. I need help to make the code work for multiple dropdown menus.
document.getElementById("dropbtn").addEventListener('click', function() {
document.getElementById("sub-menu").classList.toggle("show");
var x = document.getElementById("dropbtn").getAttribute("aria-expanded");
if (x == "true")
{
x = "false"
} else {
x = "true"
}
document.getElementById("dropbtn").setAttribute("aria-expanded", x);
});
// Close the dropdown menu if the user clicks outside of it
window.onclick = function(event) {
if (!event.target.matches('.dropbtn')) {
var dropdowns = document.getElementsByClassName("sub-menu");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
<nav>
<ul>
<li class="dropdown" id="dropdown">
<a id="dropbtn" class="dropbtn" href="#" aria-expanded="false">Toggle<span class="drop-icon" for="toggle">▾</span></a>
<ul id="sub-menu" class="sub-menu">
<li>
A
</li>
<li>
B
</li>
<li>
<a href="/c/" >C</a>
</li>
</ul>
</li>
<li class="dropdown" id="dropdown">
<a id="dropbtn" class="dropbtn" href="#" aria-expanded="false">Dropdown <span class="drop-icon" for="dropdown">▾</span></a>
<ul id="sub-menu" class="sub-menu">
<li>
D
</li>
<li>
E
</li>
<li>
F
</li>
</ul>
</li>
</ul>
</nav>
It's not reccomended to use multiple id's with the same name.
Here's my solution for your case.
<nav>
<ul>
<li class="dropdown">
<a class="dropbtn" aria-expanded="false" href="#">Toggle<span class="drop-icon">▾</span></a>
<ul class="sub-menu">
<li>
A
</li>
<li>
B
</li>
<li>
C
</li>
</ul>
</li>
<li class="dropdown">
<a class="dropbtn" aria-expanded="false" href="#">Dropdown <span class="drop-icon">▾</span></a>
<ul class="sub-menu">
<li>
D
</li>
<li>
E
</li>
<li>
F
</li>
</ul>
</li>
</ul>
</nav>
<script>
let dropbtns = document.getElementsByClassName('dropbtn');
for (let dropbtn of dropbtns) {
dropbtn.onclick = () => {
dropbtn.nextElementSibling.classList.toggle('show');
let expanded = dropbtn.getAttribute('aria-expanded');
dropbtn.setAttribute('aria-expanded', expanded == 'true' ? 'false' : 'true');
};
}
// Close the dropdown menu if the user clicks outside of it
window.onclick = function (event) {
if (!event.target.matches('.dropbtn')) {
let dropbtns = document.getElementsByClassName('dropbtn');
for (let dropbtn of dropbtns) {
dropbtn.nextElementSibling.classList.remove('show');
dropbtn.setAttribute('aria-expanded', 'false');
}
}
};
</script>
So, first of all. There can only be one unique ID per page, so your HTML is now invalid. You will have to have different IDs on your HTML elements (if you still need them at all). Below JS hopefully should work.
const dropdownTriggers = document.querySelectorAll('.dropbtn');
[...dropdownTriggers].forEach((trigger) => {
trigger.addEventListener('click', (e) => {
const triggerClicked = e.target;
const dropdownMenu = triggerClicked.nextElementSibling;
const isDropdownShown = Boolean(triggerClicked.getAttribute('aria-expanded'));
dropdownMenu.classList.toggle('show', !isDropdownShown);
triggerClicked.setAttribute('aria-expanded', isDropdownShown);
})
})
document.addEventListener('click', (e) => {
if (!event.target.matches('.dropbtn')) {
[...dropdownTriggers].forEach((trigger) => {
const dropdownMenu = trigger.nextElementSibling;
dropdownMenu.classList.toggle('show', false);
trigger.setAttribute('aria-expanded', false);
})
}
});
I have been staring at this code for far too long, unfortunately I do not see the problem.
I am trying to get the active menu entry highlighted when the relevant div gets scrolled into view. But nothing is happening and no errors are being thrown in the console.
My menu html:
<section class="LeftAnchorNav" style="display: block;">
<nav id="LeftAnchorNav">
<div class="container" style="padding-left: 50px;">
<div class="col-md-4 LeftAnchorNavWrapper">
<ul class="LeftAnchorNavMenu">
<li class="leftanchorlink">
<a class="leftlink" href="#20a51af3-f8b0-4ef9-ba73-cf3cd0a321b9">About us</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#d736bc13-a2a7-48d4-8ecc-75b9a17f801b">Demo Center</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#545a6339-87e4-41ed-ad51-70c3788cedee">Testimonial</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#9355324a-6219-4300-ae97-aa77bf67dab4">Newsletter</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#0c70b0db-3e70-4faa-ab98-154b4eae498e">Blog</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#4903bc53-b862-42f0-a600-e21061204e42">Contact</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#002f6fd7-758b-4b27-8c75-0ce087ee826a">Solution Finder</a>
</li>
</ul>
</div>
</div>
</nav>
</section>
An example div:
<div class="block anchorblock col-lg-12 col-md-12 col-sm-12 col-xs-12 span12 "><div id="20a51af3-f8b0-4ef9-ba73-cf3cd0a321b9"></div>
</div>
My jquery/js:
if ($('.LeftAnchorNav').length > 0) {
// prepare the variables
var lastID;
var anchorMenu = $(".LeftAnchorNavMenu");
var anchorMenuHeight = anchorMenu.outerHeight() + 100;
var anchorMenuItems = anchorMenu.find(".leftlink");
var anchorMenuItemsTarget = anchorMenuItems.map(function () {
var item = $($(this).attr("href"));
if (item.length) { return item; }
});
// bind everything to the scrolling
$(window).scroll(function () {
// get anchornav container scroll position and add buffer
var fromTop = $(this).scrollTop() + anchorMenuHeight + 300;
// get ID of the current scroll item
var currentItem = anchorMenuItemsTarget.map(function () {
if ($(this).offset().top < fromTop)
return this;
});
// get the ID of the current element
currentItem = currentItem[currentItem.length - 1];
var id = currentItem && currentItem.length ? currentItem[0].id : "";
if (lastID !== id) {
lastID = id;
// Set/remove active class
anchorMenuItems.removeClass("highlightleftnavactive")
anchorMenuItems.filter("[href='#" + id + "']").addClass("highlightleftnavactive");
}
});
}
It's quite fiddly to do the arithmetic for scrolling so this snippet uses IntersectionObserver instead. This has the added benefit of less processing overhead as it just gets informed when the elements come in or go out of view, not every time the user scrolls a bit.
It sets up the observer to observe when any of the relevant elements come into or go out of the viewport. When alerted to that it adds or removes the highlighting class to the related navbar link.
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/css/bootstrap.min.css" integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap#4.5.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ho+j7jyWK8fNQe+A12Hb8AhRq26LrZ/JpcUGGOn+Y7RsweNrtN/tE3MoK7ZeZDyx" crossorigin="anonymous"></script>
<style>
.LeftAnchorNav {
position: fixed;
z-index:1;
}
.tall {
width: 100vw;
height: 100vh;
background-image: linear-gradient(cyan, magenta, yellow, black);
}
.highlightleftnavactive {
background-color: yellow;
}
</style>
</head>
<section class="LeftAnchorNav" style="display: block;">
<nav id="LeftAnchorNav">
<div class="container" style="padding-left: 50px;">
<div class="col-md-4 LeftAnchorNavWrapper">
<ul class="LeftAnchorNavMenu">
<li class="leftanchorlink">
<a class="leftlink" href="#20a51af3-f8b0-4ef9-ba73-cf3cd0a321b9">About us</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#d736bc13-a2a7-48d4-8ecc-75b9a17f801b">Demo Center</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#545a6339-87e4-41ed-ad51-70c3788cedee">Testimonial</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#9355324a-6219-4300-ae97-aa77bf67dab4">Newsletter</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#0c70b0db-3e70-4faa-ab98-154b4eae498e">Blog</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#4903bc53-b862-42f0-a600-e21061204e42">Contact</a>
</li>
<li class="leftanchorlink">
<a class="leftlink" href="#002f6fd7-758b-4b27-8c75-0ce087ee826a">Solution Finder</a>
</li>
</ul>
</div>
</div>
</nav>
</section>
<div class="tall"></div>
<div class="block anchorblock col-lg-12 col-md-12 col-sm-12 col-xs-12 span12 "><div id="20a51af3-f8b0-4ef9-ba73-cf3cd0a321b9">
An example block coming into and going out of view it belongs to the About us link in the navbar</div>
</div>
<div class="tall"></div>
<script>
let callback = (entries) => {
entries.forEach(entry => {
let id = entry.target.firstChild.id;
let leftLink = document.querySelector("a.leftlink[href='#"+ id + "']");
if (entry.isIntersecting) { leftLink.classList.add('highlightleftnavactive');}
else { leftLink.classList.remove('highlightleftnavactive');}
});
};
const observer = new IntersectionObserver(callback);
const anchorBlocks = document.querySelectorAll('.anchorblock');
anchorBlocks.forEach( (anchorBlock) => {
observer.observe(anchorBlock);
});
</script>
I've been trying to create a very basic dropdown menu but am unable to get the hide on outside click part right. I've gone through several iterations yet always end up unable to dismiss it correctly or close other opened dropdowns when another is activated. Here is my latest variation.
function Menu(trigger) {
let expanded = trigger.getAttribute("aria-expanded") === "true" || false;
let menu = trigger.nextElementSibling;
trigger.setAttribute("aria-expanded", !expanded);
menu.hidden = !menu.hidden;
}
const menuTriggers = document.querySelectorAll(".menu-trigger");
menuTriggers.forEach((menuTrigger) => {
menuTrigger.addEventListener("click", () => {
Menu(menuTrigger);
});
});
<button class="menu-trigger" aria-expanded="false">Menu</button>
<ul class="menu" hidden>
<li class="menu-item">
<a class="menu-link" href="">Books</a>
</li>
<li class="menu-item">
<a class="menu-link" href="">Authors</a>
</li>
<li class="menu-item">
<a class="menu-link menu-link-separator" href="">Genres</a>
</li>
<li class="menu-item">
<a class="menu-link" href="">Themes</a>
</li>
</ul>
You'll have to listen for a mousedown event on the window object to detect the off-click and only close the drop down menu if the event.target is not contained by the drop down menu itself.
function Menu(trigger)
{
let expanded = trigger.getAttribute("aria-expanded") === "true" || false;
let menu = trigger.nextElementSibling;
trigger.setAttribute("aria-expanded", !expanded);
menu.hidden = !menu.hidden;
if(!menu.hidden)
{
setTimeout(() =>
{
let eventType = "mousedown";
let handler = (e) =>
{
if (trigger !== e.target && !menu.contains(e.target))
{
Menu(trigger);
}
window.removeEventListener(eventType, handler, false);
};
window.addEventListener(eventType, handler, false);
}, 0);
}
}
const menuTriggers = document.querySelectorAll(".menu-trigger");
menuTriggers.forEach((menuTrigger) =>
{
menuTrigger.addEventListener("click", () =>
{
Menu(menuTrigger);
});
});
<div style="display:inline-block">
<button class="menu-trigger" aria-expanded="false">Menu</button>
<ul class="menu" hidden>
<li class="menu-item">
<a class="menu-link" href="https://amazon.com">Books</a>
</li>
<li class="menu-item">
<a class="menu-link" href="">Authors</a>
</li>
<li class="menu-item">
<a class="menu-link menu-link-separator" href="">Genres</a>
</li>
<li class="menu-item">
<a class="menu-link" href="">Themes</a>
</li>
</ul>
</div>
The onclick event on button with role="menuitem" is not getting triggered but mouseover is getting triggered.
I have tried various things like instead of using an external function, I simplied made an alert but no benefit.
{{#each docs}}
<li class="note" id={{this.time}}>
<a href="#">
<div class="container">
<div class="more" id={{this.time}} onclick="openMenu(event)">
<button id={{this.time}} class="more-btn">
<span id={{this.time}} class="more-dot"></span>
<span id={{this.time}} class="more-dot"></span>
<span id={{this.time}} class="more-dot"></span>
</button>
<div class="more-menu">
<div class="more-menu-caret">
<div class="more-menu-caret-outer"></div>
<div class="more-menu-caret-inner"></div>
</div>
<ul class="more-menu-items" tabindex="-1" role="menu" aria-labelledby="more-btn"
aria-hidden="true">
<li class="more-menu-item" role="presentation">
<button type="button" class="more-menu-btn" role="menuitem" onclick="updateNote(event)">Update me</button>
</li>
<li class="more-menu-item" role="presentation" >
<button type="button" id ="rome" class="more-menu-btn" role="menuitem" onclick="deleteNote(event)" >Delete</button>
</li>
<li class="more-menu-item" role="presentation">
<button type="button" class="more-menu-btn" role="menuitem" onclick="toggleReminder(event)">
{{#if this.remind}}
Unset Reminder
{{else}}
Set reminder
{{/if}}
</button>
</li>
</ul>
</div>
</div>
</div>
Here is the external js file:
function openMenu(event) {
var elList = document.getElementsByClassName('more');
var el;
for (var i = 0; i < elList.length; i++) {
el = elList[i];
if (el.id === event.target.id)
break;
}
var btn = el.querySelector('.more-btn');
var menu = el.querySelector('.more-menu');
var menuItems = el.querySelector('.more-menu-items');
var visible = false;
function showMenu(e) {
e.preventDefault();
if (!visible) {
visible = true;
el.classList.add('show-more-menu');
menu.setAttribute('aria-hidden', false);
menuItems.setAttribute('aria-hidden', false);
document.addEventListener('mousedown', hideMenu, false);
}
}
function hideMenu(e) {
if (btn.contains(e.target)) {
return;
}
if (visible) {
visible = false;
el.classList.remove('show-more-menu');
menu.setAttribute('aria-hidden', true);
menuItems.setAttribute('aria-hidden', true);
document.removeEventListener('mousedown', hideMenu);
}
}
btn.addEventListener('click', showMenu, false);
el.addEventListener('mouseleave', hideMenu, false)
$('#rome').click(alertHye);
}
function deleteNote(e) {
console.log(e.target)
}
What's happening is nothing, not even a console error. Expected is a console log.
I have the below jQuery filter code: And i am trying fo tilter only the visible elements (to aply the filter if another filter was applied.)
$(window).bind("load", function() {
$(".filters li").on("click", function() {
id = ($(this).data("id") + '').split(',');
filter = $(this).data("filter");
$("#hotel-list .box").hide();
id[0] == "all" && $("#hotel-list .box").show() || id.forEach(function(v) {
$('#hotel-list .box[data-' + filter + '*="' + v.trim() + '"]').show();
});
return false;
});
HTML code of the Filters:
<div class="panel style1 arrow-right">
<h4 class="panel-title"><a class="collapsed" data-toggle="collapse" href="#rating-filter">Clasificare</a></h4>
<div id="rating-filter" class="filters panel-collapse collapse in">
<div class="panel-content">
<ul class="check-square filters-option">
<li data-id="2, 3, 4, 5" data-filter="stars">
Toate<small class="totals"></small>
</li>
<li data-id="2, 3" data-filter="stars">
<div data-placement="bottom" data-toggle="tooltip" class="stars-3-container" title="" data-original-title="3 Stele"><span style="width: 80%;" class="stars-3"></span></div><small class="total-three"></small>
</li>
<li data-id="4" data-filter="stars">
<div data-placement="bottom" data-toggle="tooltip" class="stars-4-container" title="" data-original-title="4 Stele"><span style="width: 80%;" class="stars-4"></span></div><small class="total-four"></small>
</li>
<li data-id="5" data-filter="stars">
<div data-placement="bottom" data-toggle="tooltip" class="stars-5-container" title="" data-original-title="5 Stele"><span style="width: 80%;" class="stars-5"></span></div><small class="total-five"></small>
</li>
</ul>
</div>
</div>
</div>
<!-- Board Type -->
<div class="panel style1 arrow-right">
<h4 class="panel-title"><a class="collapsed" data-toggle="collapse" href="#board-filter">Tip Masa</a></h4>
<div id="board-filter" class="filters panel-collapse collapse in">
<div class="panel-content">
<ul class="check-square filters-option">
<li data-id='Room, No, Breakfast, Half Board, Full Board, Inc, Self, Ultra' data-filter="board">
Toate<small class="total"></small>
</li>
<li data-id='Room, No' data-filter="board">
Room Only<small class="total-ro"></small>
</li>
<li data-id='Breakfast' data-filter="board">
Breakfast<small class="total-bb"></small>
</li>
<li data-id='Half' data-filter="board">
Half Board<small class="total-hb"></small>
</li>
<li data-id='Full Board, Full board' data-filter="board">
Full Board<small class="total-fb"></small>
</li>
<li data-id='Inc, Ultra' data-filter="board">
All Inclusive<small class="total-ai"></small>
</li>
<li data-id='Self'data-filter="board">
Self Catering<small class="total-sc"></small>
</li>
</ul>
</div>
</div>
</div>
And the html elemets filtered are:
<div id="hotel-list" class="hotel-list listing-style3 hotel">
<article class="box" data-board="Room Only" data-stars="3">
<div>Content here</div>
</article>
<article class="box" data-board="Breakfast" data-stars="4">
<div>Content here</div>
</article>
.....
</div>
JSFIDDLE
Have any ideea on how can i filter only the visible elements ?
Try something like this, change
from
filter = $(this).data("filter");
to
filter = $(this).data("filter").filter(':visible');
You can do a filter like
jQuery(function ($) {
$(".filters li").on("click", function () {
$(this).closest('ul').find('.selected').removeClass('selected');
$(this).addClass('selected');
var filters = $(".filters li.selected").map(function () {
var filter = $(this).data("filter");
return[ $.map(($(this).data("id") + '').split(','), function (v) {
return '.box[data-' + filter + '*="' + v.trim() + '"]'
})];
}).get();
var $els = $('#hotel-list .box');
if (filters.length) {
$els.show();
$.each(filters, function (rel, list) {
console.log(list)
$els.not(list.join()).hide();
});
} else {
$els.show();
}
});
});
Demo: Fiddle