Доступный образец перетаскивания не работает
Я создал образец перетаскивания, используя эту ссылку. В этом примере пользователь может перетащить любой элемент из первой строки и поместить его в любой второй элемент строки. При отбрасывании перетаскиваемого элемента целевой объект удаляется. Пользователь может выбрать элемент, используя пробел, и может перетащить его во второй ряд, используя клавишу ввода. Я протестировал свое приложение, используя jaws и nvda, после выбора любого элемента фокус не перемещался на следующую строку, тогда как без этих программ чтения с экрана образец работает нормально. После адресной строки фокус направляется непосредственно на второй элемент первого ряда, а не на первый элемент.
(function () {
//exclude older browsers by the features we need them to support
//and legacy opera explicitly so we don't waste time on a dead browser
if (!document.querySelectorAll ||
!('draggable' in document.createElement('span')) ||
window.opera
) {
return;
}
//get the collection of draggable targets and add their draggable attribute
for (var targets = document.querySelectorAll('[data-draggable="target"]'),
len = targets.length,
i = 0; i < len; i++) {
targets[i].setAttribute('aria-dropeffect', 'none');
}
//get the collection of draggable items and add their draggable attributes
for (var
items = document.querySelectorAll('[data-draggable="item"]'),
len = items.length,
i = 0; i < len; i++) {
items[i].setAttribute('draggable', 'true');
items[i].setAttribute('aria-grabbed', 'false');
items[i].setAttribute('tabindex', '0');
}
//dictionary for storing the selections data
//comprising an array of the currently selected items
//a reference to the selected items' owning container
//and a refernce to the current drop target container
var selections = {
items: null,
owner: null,
droptarget: null
};
//function for selecting an item
function addSelection(item) {
//if the owner reference is still null, set it to this item's parent
//so that further selection is only allowed within the same container
if (!selections.owner) {
selections.owner = item.parentNode;
}
//or if that's already happened then compare it with this item's parent
//and if they're not the same container, return to prevent selection
else if (selections.owner != item.parentNode) {
return;
}
//set this item's grabbed state
item.setAttribute('aria-grabbed', 'true');
for (var
items = document.querySelectorAll('[data-draggable="item"][aria-grabbed="false"]'),
len = items.length,
i = 0; i < len; i++) {
items[i].removeAttribute('draggable');
items[i].removeAttribute('aria-grabbed');
items[i].removeAttribute('tabindex');
}
//add it to the items array
selections.items = item;
}
//function for unselecting an item
function removeSelection(item) {
//reset this item's grabbed state
item.setAttribute('aria-grabbed', 'false');
//then find and remove this item from the existing items array
// for (var len = selections.items.length, i = 0; i < len; i++) {
// if (selections.items[i] == item) {
// selections.items.splice(i, 1);
// break;
// }
// }
selections.items = null;
}
//function for resetting all selections
function clearSelections() {
//if we have any selected items
if (selections.items) {
//reset the owner reference
selections.owner = null;
//reset the grabbed state on every selected item
// for (var len = selections.items.length, i = 0; i < len; i++) {
// selections.items[i].setAttribute('aria-grabbed', 'false');
// }
selections.items.setAttribute('aria-grabbed', 'false');
//then reset the items array
selections.items = null;
}
}
//shorctut function for testing whether a selection modifier is pressed
function hasModifier(e) {
return (e.ctrlKey || e.metaKey || e.shiftKey);
}
//function for applying dropeffect to the target containers
function addDropeffects() {
//apply aria-dropeffect and tabindex to all targets apart from the owner
for (var len = targets.length, i = 0; i < len; i++) {
if (
targets[i] != selections.owner &&
targets[i].getAttribute('aria-dropeffect') == 'none'
) {
targets[i].setAttribute('aria-dropeffect', 'move');
targets[i].setAttribute('tabindex', '0');
}
}
//remove aria-grabbed and tabindex from all items inside those containers
for (var len = items.length, i = 0; i < len; i++) {
if (
items[i].parentNode != selections.owner &&
items[i].getAttribute('aria-grabbed')
) {
items[i].removeAttribute('aria-grabbed');
items[i].removeAttribute('tabindex');
}
}
}
//function for removing dropeffect from the target containers
function clearDropeffects() {
//if we have any selected items
if (selections.items) {
//reset aria-dropeffect and remove tabindex from all targets
for (var len = targets.length, i = 0; i < len; i++) {
if (targets[i].getAttribute('aria-dropeffect') != 'none') {
targets[i].setAttribute('aria-dropeffect', 'none');
targets[i].removeAttribute('tabindex');
}
}
//restore aria-grabbed and tabindex to all selectable items
//without changing the grabbed value of any existing selected items
for (var len = items.length, i = 0; i < len; i++) {
if (!items[i].getAttribute('aria-grabbed') && items[i].getAttribute('draggable') == 'true') {
items[i].setAttribute('aria-grabbed', 'false');
items[i].setAttribute('tabindex', '0');
} else if (items[i].getAttribute('aria-grabbed') == 'true') {
items[i].setAttribute('tabindex', '0');
}
}
}
}
//shortcut function for identifying an event element's target container
function getContainer(element) {
do {
if (element.nodeType == 1 && element.getAttribute('aria-dropeffect')) {
return element;
}
}
while (element = element.parentNode);
return null;
}
//mousedown event to implement single selection
document.addEventListener('mousedown', function (e) {
//if the element is a draggable item
if (e.target.getAttribute('draggable')) {
//clear dropeffect from the target containers
clearDropeffects();
//if the multiple selection modifier is not pressed
//and the item's grabbed state is currently false
if (!hasModifier(e) &&
e.target.getAttribute('aria-grabbed') == 'false'
) {
//clear all existing selections
clearSelections();
//then add this new selection
addSelection(e.target);
}
}
//else [if the element is anything else]
//and the selection modifier is not pressed
else if (!hasModifier(e)) {
//clear dropeffect from the target containers
clearDropeffects();
//clear all existing selections
clearSelections();
}
//else [if the element is anything else and the modifier is pressed]
else {
//clear dropeffect from the target containers
clearDropeffects();
}
}, false);
//mouseup event to implement multiple selection
document.addEventListener('mouseup', function (e) {
//if the element is a draggable item
//and the multipler selection modifier is pressed
if (e.target.getAttribute('draggable') && hasModifier(e)) {
//if the item's grabbed state is currently true
if (e.target.getAttribute('aria-grabbed') == 'true') {
//unselect this item
removeSelection(e.target);
//if that was the only selected item
//then reset the owner container reference
if (!selections.items) {
selections.owner = null;
}
}
//else [if the item's grabbed state is false]
else {
//add this additional selection
addSelection(e.target);
}
}
}, false);
//dragstart event to initiate mouse dragging
document.addEventListener('dragstart', function (e) {
//if the element's parent is not the owner, then block this event
if (selections.owner != e.target.parentNode) {
e.preventDefault();
return;
}
//[else] if the multiple selection modifier is pressed
//and the item's grabbed state is currently false
if (
hasModifier(e) &&
e.target.getAttribute('aria-grabbed') == 'false'
) {
//add this additional selection
addSelection(e.target);
}
//we don't need the transfer data, but we have to define something
//otherwise the drop action won't work at all in firefox
//most browsers support the proper mime-type syntax, eg. "text/plain"
//but we have to use this incorrect syntax for the benefit of IE10+
e.dataTransfer.setData('text', '');
//apply dropeffect to the target containers
addDropeffects();
}, false);
//keydown event to implement selection and abort
document.addEventListener('keydown', function (e) {
//if the element is a grabbable item
if (e.target.getAttribute('aria-grabbed')) {
//Space is the selection or unselection keystroke
if (e.keyCode == 32) {
//if the multiple selection modifier is pressed
if (hasModifier(e)) {
////alert("278");
//if the item's grabbed state is currently true
if (e.target.getAttribute('aria-grabbed') == 'true') {
//if this is the only selected item, clear dropeffect
//from the target containers, which we must do first
//in case subsequent unselection sets owner to null
////alert("284");
if (selections.items) {
clearDropeffects();
}
//unselect this item
removeSelection(e.target);
//if we have any selections
//apply dropeffect to the target containers,
//in case earlier selections were made by mouse
if (selections.items) {
addDropeffects();
}
//if that was the only selected item
//then reset the owner container reference
if (!selections.items) {
selections.owner = null;
}
}
//else [if its grabbed state is currently false]
else {
////alert("308");
//add this additional selection
addSelection(e.target);
//apply dropeffect to the target containers
addDropeffects();
}
}
//else [if the multiple selection modifier is not pressed]
//and the item's grabbed state is currently false
else if (e.target.getAttribute('aria-grabbed') == 'false') {
//alert("320");
//clear dropeffect from the target containers
clearDropeffects();
//clear all existing selections
clearSelections();
//add this new selection
addSelection(e.target);
//apply dropeffect to the target containers
addDropeffects();
}
//else [if modifier is not pressed and grabbed is already true]
else {
//alert("336");
//apply dropeffect to the target containers
addDropeffects();
}
//then prevent default to avoid any conflict with native actions
e.preventDefault();
}
//Modifier + M is the end-of-selection keystroke
if (e.keyCode == 77 && hasModifier(e)) {
//if we have any selected items
if (selections.items) {
//apply dropeffect to the target containers
//in case earlier selections were made by mouse
addDropeffects();
//if the owner container is the last one, focus the first one
if (selections.owner == targets[targets.length - 1]) {
targets[0].focus();
}
//else [if it's not the last one], find and focus the next one
else {
for (var len = targets.length, i = 0; i < len; i++) {
if (selections.owner == targets[i]) {
targets[i + 1].focus();
break;
}
}
}
}
//then prevent default to avoid any conflict with native actions
e.preventDefault();
}
}
//Escape is the abort keystroke (for any target element)
if (e.keyCode == 27) {
//if we have any selected items
if (selections.items) {
//clear dropeffect from the target containers
clearDropeffects();
//then set focus back on the last item that was selected, which is
//necessary because we've removed tabindex from the current focus
//>>
//selections.items[selections.items.length - 1].focus();
//clear all existing selections
clearSelections();
//but don't prevent default so that native actions can still occur
}
}
}, false);
//related variable is needed to maintain a reference to the
//dragleave's relatedTarget, since it doesn't have e.relatedTarget
var related = null;
//dragenter event to set that variable
document.addEventListener('dragenter', function (e) {
related = e.target;
}, false);
//dragleave event to maintain target highlighting using that variable
document.addEventListener('dragleave', function (e) {
//get a drop target reference from the relatedTarget
var droptarget = getContainer(related);
//if the target is the owner then it's not a valid drop target
if (droptarget == selections.owner) {
droptarget = null;
}
//if the drop target is different from the last stored reference
//(or we have one of those references but not the other one)
if (droptarget != selections.droptarget) {
//if we have a saved reference, clear its existing dragover class
if (selections.droptarget) {
selections.droptarget.className =
selections.droptarget.className.replace(/ dragover/g, '');
}
//apply the dragover class to the new drop target reference
if (droptarget) {
droptarget.className += ' dragover';
}
//then save that reference for next time
selections.droptarget = droptarget;
}
}, false);
//dragover event to allow the drag by preventing its default
document.addEventListener('dragover', function (e) {
//if we have any selected items, allow them to be dragged
if (selections.items) {
e.preventDefault();
}
}, false);
//dragend event to implement items being validly dropped into targets,
//or invalidly dropped elsewhere, and to clean-up the interface either way
document.addEventListener('dragend', function (e) {
//if we have a valid drop target reference
//(which implies that we have some selected items)
if (selections.droptarget) {
//append the selected items to the end of the target container
// for (var len = selections.items.length, i = 0; i < len; i++) {
// selections.droptarget.appendChild(selections.items[i]);
// }
//selections.droptarget.appendChild(selections.items);
selections.items.removeAttribute("data-draggable");
selections.items.setAttribute('draggable', 'false');
selections.items.removeAttribute('aria-grabbed');
selections.items.setAttribute('tabindex', '-1');
selections.droptarget.parentNode.insertBefore(selections.items, selections.droptarget.nextElementSibling)
selections.droptarget.parentNode.removeChild(selections.droptarget);
for (var
items = document.querySelectorAll('[data-draggable="item"]'),
len = items.length,
i = 0; i < len; i++) {
items[i].setAttribute('draggable', 'true');
items[i].setAttribute('aria-grabbed', 'false');
items[i].setAttribute('tabindex', '0');
}
//selections.droptarget = selections.items;
//prevent default to allow the action
e.preventDefault();
}
//if we have any selected items
if (selections.items) {
//clear dropeffect from the target containers
clearDropeffects();
//if we have a valid drop target reference
if (selections.droptarget) {
//reset the selections array
clearSelections();
//reset the target's dragover class
selections.droptarget.className =
selections.droptarget.className.replace(/ dragover/g, '');
//reset the target reference
selections.droptarget = null;
}
}
}, false);
//keydown event to implement items being dropped into targets
document.addEventListener('keydown', function (e) {;
//if the element is a drop target container
if (e.target.getAttribute('aria-dropeffect')) {
//Enter or Modifier + M is the drop keystroke
if (e.keyCode == 13 || (e.keyCode == 77 && hasModifier(e))) {
//append the selected items to the end of the target container
// e.target.appendChild(selections.items);
selections.items.removeAttribute("data-draggable");
selections.items.setAttribute('draggable', 'false');
selections.items.removeAttribute('aria-grabbed');
selections.items.setAttribute('tabindex', '-1');
e.target.parentNode.insertBefore(selections.items, e.target.nextElementSibling);
e.target.parentNode.removeChild(e.target);
for (var
items = document.querySelectorAll('[data-draggable="item"]'),
len = items.length, i = 0; i < len; i++) {
items[i].setAttribute('draggable', 'true');
items[i].setAttribute('aria-grabbed', 'false');
items[i].setAttribute('tabindex', '0');
}
//e.target = selections.items;
//clear dropeffect from the target containers
clearDropeffects();
//then set focus back on the last item that was selected, which is
//necessary because we've removed tabindex from the current focus
//>>
//selections.items[selections.items.length - 1].focus();
//reset the selections array
clearSelections();
//prevent default to to avoid any conflict with native actions
e.preventDefault();
}
}
}, false);
})();
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.boxes {
float: left;
width: 100px;
height: 35px;
margin: 10px;
padding: 10px;
border: 1px solid black;
}
.clearfix {
content: '';
display: block;
clear: both;
}
img {
display: inline-block;
width: 100px;
margin: 10px;
vertical-align: top;
}
/* drop target state */
[data-draggable="target"][aria-dropeffect="move"] {
border-color: #68b;
background: #fff;
}
/* drop target focus and dragover state */
[data-draggable="target"][aria-dropeffect="move"]:focus,
[data-draggable="target"][aria-dropeffect="move"].dragover {
outline: none;
box-shadow: 0 0 0 1px #fff, 0 0 0 3px #68b;
}
/* draggable items */
img {
display: inline-block;
width: 100px;
margin: 10px;
}
/* items focus state */
[data-draggable="item"]:focus {
outline: none;
box-shadow: 0 0 0 2px #68b, inset 0 0 0 1px #ddd;
}
/* items grabbed state */
[data-draggable="item"][aria-grabbed="true"] {
border: 5px solid #8ad;
color: #fff;
}
<body>
<div>
<img src="images/41.png" alt="img1" data-draggable="item" role="img">
<img src="images/165.png" alt="img2" data-draggable="item" role="img">
<img src="images/135.png" alt="img3" data-draggable="item" role="img">
<img src="images/155.png" alt="img4" data-draggable="item" role="img">
</div>
<div>
<img src="images/img5.png" alt="img5" data-draggable="target" role="img">
<img src="images/img6.png" alt="img6" data-draggable="target" role="img">
<img src="images/img7.png" alt="img7" data-draggable="target" role="img">
<img src="images/img8.png" alt="img8" data-draggable="target" role="img">
</div>
<div>
<h2 id="kbdinstructions">Keyboard Instructions</h2>
<ol class="instructions">
<li>Use the <b>Tab</b> to move across images.</li>
<li>Select the item you want using <b>Spacebar</b>.</li>
<li>Use the <b>Tab</b> again to move across images.</li>
<li>Press <b>Enter</b> to move selected image to the target image.</li>
</ol>
</div>
</body>
Почему это происходит?