I've setup interact.js draggable as -
interact('.draggable')
.draggable({
onstart: (event: Interact.InteractEvent) => onstartRef.current(event),
onmove: (event: Interact.InteractEvent) => onmoveRef.current(event),
onend: (event: Interact.InteractEvent) => onendRef.current(event)
})
I was trying to add support for a11y with keyboard drag with interact.js but could not find anything in documentation for it. How to bind "dragStart", "dragMove" and "dragEnd" events with keyboard events.
I could find a github issue but is there more information - https://github.com/taye/interact.js/issues/275.
I'm starting with listening to keyboard arrow-left, right, up, down, and willing to start "onStart", "onEnd" and "onMove".
switch (event.key) {
case 'ArrowLeft':
It’s quite likely that the dragging is not essential to your application, and that other interactions, like context menus, can achieve the same goal.
Don’t try to force it and replicate the exact same interaction, just with keyboard. Steering an element across the screen by means of arrow keys might be playful at first, but is cumbersome when trying to get a job done.
Plus, it still is inaccessible to users with vision impairments.
The WCAG 2.2 explicitly demand a single pointer interaction
Success Criterion 2.5.7 Dragging Movements (Level AA): All functionality that uses a dragging movement for operation can be achieved by a single pointer without dragging, unless dragging is essential or the functionality is determined by the user agent and not modified by the author.
The currently sole sufficient technique to satisfy that criterion is listing some examples:
Example 1
A list of items can be re-ordered by picking up an item and dragging it upwards or downwards. Other elements move dynamically to open a gap where the picked-up target can be dropped. After a single pointer activation, the list items display up and down arrows which allow a step-wise re-ordering of the list via single pointer inputs (taps or clicks at the up or down arrow).
A vertical priority list indicates the priority of items listed. Each item can be ‘picked up’ with a pointer and dragged up or down to another position. The other list items rearrange dynamically. To the left of each list item, a number in a text field shows the current priority position. For any of the items, users can put in another number. This leads to a dynamic reordering and renumbering of the priority list.
In a Kanban implementation for process management, tasks can be dragged horizontally from one ‘swimming lane’ to another in order to change the status of tasks (for example, to change the status of a task from “in process” to “completed”). One or several items in a lane can be selected by a single tap or click. A single pointer activation of a drop-down menu labelled “Move selected items to” offers a selection of drop targets (other lanes). A further single pointer activation over the desired menu item moves targets to the specified lane.
In a radial control widget, the visual indicator of the current value of the control can be dragged to a different position. Users can also click or tap on another position of the radial control to change the value.
In windows, you can drag an item onto your trash icon, or you can right click it and select “Delete”. The context menu is keyboard accessible.
Related
I'm using Vue.Draggable to implement drag and drop sorting on a table element. Some of the draggable elements in the table are quite tall (tbody tags with many rows), so I would like to reduce the height of the so-called "ghost" element while the drag is happening.
To clarify, the drag "ghost" element is the copy of the dragging element that changes position in the list in real time. It's the blue element in this example:
https://sortablejs.github.io/Vue.Draggable/#/simple
(I'm not talking about the "drag image:" the copy of the dragging element that sticks to the mouse cursor)
I have tried hiding all but the first table row with CSS:
.sortable-drag tr:not(:first-child) {
display: none;
}
But the library uses the height of the dragged element to calculate its position relative to the mouse cursor, so this makes the dragging process buggy and unpredictable.
Vue.Draggable is built on Sortable JS and it fires a number of events, so I'm wondering if there's a way to customize the element when the drag begins, if not some option in Vue.Draggable/Sortable that I can use.
The approach above (hiding all but the first row with CSS) actually works as expected, but not without some extra configuration. The "buggy and unpredictable" behavior I was seeing was from an unrelated feature in Sortable JS:
Sortable JS Wiki: direction option
Sortable will try to automatically detect which direction is used by default, but it is best that you give this value yourself.
Once I specified that the table should only sort vertically (by setting the direction="vertical" prop on my Draggable component), the drag ghost started working the way I had hoped.
WCAG has a great design pattern for implementing Tabs with Tabpanels here
It uses a "roving tab index", and keyboard arrow keys to cycle between tabs.
This works great, but I recently received a request to make a tab/tabpanel widget that only shows one tab at a time, and has "previous" and "next" arrow buttons to cycle through the tabs.
Visually, the arrow buttons are on either side of the active tab. But in terms of tab-order, the WCAG design pattern specifically states that the tabpanel associated with the active tab should follow it in tab order.
Would it violate any accessibility rules to add these new arrow nav buttons to the widget, as long as I assure that their tab order is before or after the tabs/tabpanels?
I figure the keyboard functionality could remain as it currently is (using arrow keys to cycle between tabs)
I'm a blind screen reader user.
I would maintain left/right arrow keys to navigate through the tabs as usual, and don't make your arrow buttons focusable at all.
My reasoning behind this is the following:
Your arrow buttons control scrolling and scrolling is pure visual stuff
Usually, as a screen reader user, we don't have keyboard access to buttons within scrollbars. It is completely transparent for us.
IF you take a multiline text area for example, the down arrow always goes to the next line, regardless of whether the cursor is currently at the bottom of the screen.
We don't have anything special to do, and especially we don't have to manually press the down arrow button of the scroll bar. The cursor goes to the next line, and the text content is automatically scrolled one line down if needed.
The same things happen for a tab control if there are too many tabs to fit on a single line.
In that case, in popular GUI desktop libraries, you can choose to show scroll arrow buttons, or make the tabs spand multiple lines.
It never change the way to navigate: left and right arrow keys always go to the next or previous tab, regardless of what has to happen visually.
As a bonus, usually, when you click on a scrollbar button, the focus doesn't go to the button; it immediately goes back to the real content, i.e. the text in the text area, or the currently active tab for a tab control.
This is one or more confirmation that scroll buttons shouldn't enter in tab order.
As another bonus, given that you are implementing scroll buttons on your own, you should also react to mouse wheel.
My question relates to a excellent javascript library called packery which I am using to create a masonry style layout. I am also using a sister script called draggabilly to allow the user to drag and drop (reorder) the items in the page. Currently I am doing something fairly similar to conventional usage:
http://codepen.io/desandro/pen/CKbkw
When you drag an item the interface leaves a space large enough for the element to be dropped. I wanted to place a div there temporarily to indicate that this is where the element will be dropped. All I really need is a way to get the x, y co-ordinates for where the dragged element will come to rest once released but I can't seem to figure out how to get that information.
and I am using a dragMove listener which is where I expect to display my div and provide it with the correct co-ordinates:
draggie.on( 'dragMove', function(draggieInstance, event, pointer) {
//code to display and position ghost div
});
Any assistance would be much appreciated.
In some corporate web application, I have some troubles with the :hover css selector with IE 8 users.
Basically, I have a lot of javascript, and the page sometimes is a bit slow.
Especially, my issue occurs in a sortable table (yes tables suck, but I have to live with it by now).
Within each row, I have a button that performs some operation. The button have :hover css rules to make it highlighted on mouse hover.
But in a specific case, the hover state remains, even if the mouse is no more on the element.
You can see the behavior on jsfiddle (run embbeded version with IE8): http://jsfiddle.net/stevebeauge/K9EGv/24/embedded/result/
To reproduce the problem:
Start draging an element by clicking on the "hover" button.
very quickly, drop the item (even if the drag is not visually started) elsewhere in the list
still very quickly, move your mouse out of the table
The whole operation must be done under 1.5 second.
With this scenario, the initial dragged item is in the hover state, even if the mouse is no more on the button.
Is there a way to "force" refresh of the hover state?
I was trying to answer an issue with custom drop down, but challenged by an inconsistent behavior in Chrome and Firefox.
DEMO: http://jsfiddle.net/fyeht/ [Added scroll event for more clarity]
See below image, The list items can be navigated using arrow keys.
To Reproduce the issue:
Open console in Chrome (F12)
Click on an item in the list (you would notice some events getting logged in the console)
Use down arrow key to navigate to the next item in the list
Finally, the issue is noticed when you reach the last item in the view and hitting down arrow would scroll. Check the log to see 'scroll', 'mouse enter' and 'mouse move' [check the new demo]
The issue is after reaching the end of items in view, it scrolls. Even though the mouse is untouched, it triggers mouseenter and mousemove events in Chrome. In FF, on scroll it triggers just the mouseenter which make sense.
Question(s):
Why is mousemove triggered when mouse is untouched?
Is this just browser inconsistency? Could not find documentation on events triggered when scrolling? (never knew it did)
Submitted a bug report: https://code.google.com/p/chromium/issues/detail?id=241476
In your example, I see that both Chrome and FF are firing mouseenter DOM events whenever the mouse is left hovering over the <ul> and pressing the key down triggers the browser to scroll in order to bring the selected <li> into view.
However, only Chrome is additionally triggering mousemove events. One obvious difference already in the mouseenter event objects that the two throw is that for Chrome, MouseEvent.offsetX and MouseEvent.offsetY values are included, whereas in FF these properties are undefined. So, when that enter is triggered Chrome has already decided the mouse "has moved".
Since the MouseEvent.screenX and MouseEvent.screenY event context values do not change between scroll-triggered MouseEvent instances, one could perhaps distinguish between an "artificial" mouseenter / mousemove event and an "authentic" one by storing these values from prior events.
DOM Event Specification
The DOM Level 2 Event Specification for mousemove reads:
The mousemove event occurs when the pointing device is moved while it is over an element.
The Level 3 spec (working draft) is essentially the same:
A user agent must dispatch this event when a pointing device is moved while it is over an element.
Seems like it could down to whether one interprets "is moved" relatively or not.
Also, in the section of the Level 3 spec on mouse event order, it states that when a pointer is moved into an element, it triggers mouseover, mouseenter, and mousemove, in that order. Every case that is specified there always has those three together, so perhaps one might interpret it that if you are going to trigger the mouseenter event, you should also be triggering the mousemove event which corresponds to entering the element.
This is a nice demo.
In Chrome mouse movement for elements is definitely relative. In chrome I can get keydown and scroll events only if the mouse pointer is over the scroll bar. I can also get scroll only events if i use the wheel to scroll and leave the mouse over the scroll bar. It is and isn't very odd that scroll by dragging causes "mouse move", and "mouse over" events.
Not only are mouse move and mouse over events produced in profusion by the browser they are not a very good indication of a users intent. Infact these events are a useful entropy source.
To the degree that there are some minor differences this is in the context of how useful these "micro events" are individually. To work with them you must devise a way to filter them for user intention you want to link to higher level actions. Any reasonable method you choose to make sense of these events will probably detect these move - on scroll events as garbage. This is where your point is really worth noting and taking under consternation.
A first stage would be to Filter out events based on the elements and values of coordinates. It might help to create a state machine model. You might register and registered handlers in response to other events. You've identified this case where you'd want to change responsive state if or reaction criteria if a key element has a scroll bar. IF an element or it's parent has a vertical scroll bar throw out mouse moves with relatively high X values.
You might want to also ignore mouse overs if it's fired with a mouse move in that context. It starts to become impractical to handle each of these micro event one at a time even if you are changing state by registering or deregistering handlers. More information can be extracted by creating an event sequence fifo buffer.
Register event handlers to add a new event to the buffer. You might also want to collect information from timer events in this buffer to establish more context. You might create an object that keeps a fifo in an array. This would be like a queue but not in the sense of it being a place where the events are waiting to be processed. Instead your program is waiting to calculate patterns in the buffer and based on the patterns fire higher level events, accept or reject different types of events and extend, contract or save the contents of the buffer. You can then evaluate move events for changes in x and y and also create conditions given the patter of scroll mouse over and mouse move events you've demonstrated.
I really doubt there's a browser inconsistency here. You should create a mousemove event that prints out the x and y coordinate. You'll probably see that the mouse has indeed moved a little bit. If that's the case, try using the plugin hoverIntent to eliminate issues like this.
EDIT:
Using the up and down arrow keys, I'm now able to replicate the issue. Yeah, it sure looks like some kind of bug! I bet the mousemove coordinate delta is tiny. Maybe the cursor moves one or two pixels? I would say, to overcome this, add a check to the mousemove function that compares previous mousemove's x-y coordinates to the current mousemove's x-y coordinates. Determine if it's more than just a few pixels. If so, you know it's a real mousemove. If it's less, you can chalk that up as a chrome bug.
FURTHER EDIT:
It seems like you uncovered a bug where mousemove is being fired in chrome when it probably shouldn't be. There may be workarounds that you could figure out if you hack it enough. But the best solution might be just to avoid using mousemove in this situation. In general, mousemove is one of those expensive events that should be used only when you really need it.
This is not a bug. The mousemove is relative to element that the event is attached to. In your case, you see that your mouse is not moving because you took the browser window as the reference. But for that scrolling list, whenever the list is scrolled, the mouse pointing over some element of the list moved to over different element
Imagine that you as the Earth, a cup of coffee stand still on a table as the mouse, the scrollable list as the Sun : if you (window) don't move, the position of the cup of coffee (mouse) is at the same place for you; but for the Sun (list), it will see that the Earth and the cup of coffee are both moving.