Prevent focused div from scrolling with arrow keys - javascript

I have a div component with an inner scrollbar and I'd like to prevent the up/down arrow keys from scrolling when the element is focused (mouse click on the element), as they are used for other events (e.g. zoom).
The only solution I found was to attach an event listener to the document, however, it disables all default arrow keys events, such as moving the cursor in an input field.
Here is an example (in React):
https://codesandbox.io/s/rsc-live-example-fze6z
How to reproduce:
Click with the mouse on the inner div (with the text)
Click the down arrow key -> div scrolls down
Is there a way to prevent the scroll without disabling it on the document level?
Thanks!
UPDATE:
This missing piece was adding tab-index='0' to the component (I updated the code above). Thanks #Jarvan

You can filter the events which you don't want to prevent, such as arrow event in input.
if (e.target.tagName.toLowerCase()!=="input" &&(e.key === "ArrowUp" || e.key === "ArrowDown")) {
e.preventDefault();
}
Then in input the cursor move will not be prevented.
But it won't work if the event you want to keep is some behavior in same element.
E.g. you have some children can be focus in the 'prevent arrow up/down scroll' div, you want to keep the ability to move focus between children by using arrow up/down. In that case I guess you have to implement you customize scrollbar then you have the full control, because in browser behavior I don't see a way to separate the single 'scroll' action.
Update:
If you know which component or area you want to effect, just add to that area. Like in your demo code:
<div tabindex="0"
onKeyDown={e => {
console.log(e);
if (e.key === "ArrowUp" || e.key === "ArrowDown") {
e.stopPropagation();
e.preventDefault();
console.log(e.key);
return false;
}
e.stopPropagation();
}}
>
{result}
</div>

Related

#keydown.ctrl and #keyup.ctrl event modifiers not responding to respective keyboard events vujs

I'm writing a VueJS application that will have a custom context menu that appears when the user mouses over a particular item on the page. The menu is dynamic meaning that it will change as the user hovers over different items. If the user holds down the control key the menu will be static so that they can interact with the items in the menu.
My issue is the while the #keydown.ctrl="..." event seems to respond as intended the #keyup.ctrl="..." event does not. I have looked around and tried different modifiers such as .exact and .caputure but they don't seem to solve the issue.
What does work however is changing #keyup.ctrl to #keyup.control. The .control works on the #keyup event but not for the #keydown event.
What is the difference between #keydown and #keyup that would require the use of different key modifiers?
Below is an example of what the root element looks like.
<div
tabindex="0"
#keydown.esc.exact="() => (displayContext = false)"
#keydown.ctrl.exact="() => (keepContext = true)"
#keyup.control="() => (keepContext = false)"
>
...
</div>
You can use this website to visually which keys are triggering which event: https://config.qmk.fm/#/test
or even on this one: http://keycode.info/
As you can see, you cannot have a listener on the ctrl key alone. You either need a ctrl + a or another key paired with it to effectively track it.
Here is the best implementation that we can have to track the ctrl event without any other key pressed:
hold ctrl and hover the div (#mouseover.ctrl="holdCtrlAndHover")
while holding ctrl, leave the div (#mouseleave.ctrl="holdCtrlAndLeave")
hold ctrl, hover on the div, then mouse click (#click.ctrl="hoverAndHoldCtrlThenMouseClick")
Example of the implementation: https://codesandbox.io/s/lingering-violet-q5gct?file=/src/App.vue
For a quick reference on MouseEvent.ctrlKey, here it is: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/ctrlKey
Here is a quick explanation of the differences on the event key listeners: onKeyPress Vs. onKeyUp and onKeyDown
Good to know: keypress event is now deprecated
There may be a couple of problems related to this.
Capturing keydown events (from vue template) is most reliable when you are trying to capture keydowns of an input (input, select or textarea) element. Because when you focus on the element all keyboard events are triggered on the this element. In your example you have a few divs on the page and these may not be the active element. A suitable workaround for this would be to capture all keydown or key keyup events on the document.body and checking if the event.target matches your criteria. Another benefit is you can keep your template code simpler (especially when you have many contextmenu areas.)
mounted() {
this.keepContext = false;
document.body.addEventListener('keydown', event => {
if (event.key == 'Control') {
this.keepContext = true;
}
});
document.body.addEventListener('keyup', event => {
if (event.key == 'Control') {
this.keepContext = false;
} else if (event.key == 'Escape') {
// hide the contextmenu
}
});
}
Another subject that you raised, the difference between #keydown.ctrl and #keydown.control.
The first: the .ctrl event modifier is usually used in combination with another key, (example: '#keydown.ctrl.s="autoSave"')
The second: .control is fired when you press the key control and cannot be used in combination with another key.
I have experimented with these two setups:
<input
#keydown.control="logKeydown"
#keyup.control="logKeyup">
>
and this
<input
#keydown.ctrl="logKeydown"
#keyup.ctrl="logKeyup">
>
And the setup with #keydown.control does works reliably. The #keyup.ctrl does not work reliably (sometimes it misses the keydown, sometimes it misses the keyup).
All in all I'd recommend listening to keyboard events on the document.body when using non-input elements. Second best is using the .control modifier (instead of the .ctrl) modifier.

How to listen to click event when scroll button is clicked using jQuery

Is it possible to select the scroll button and assign it an attribute value when it is clicked. I am aware that pseudo elements cannot be selected in jQuery. Any workaround solution possible for this?
Did you mean mouse's middle button?
https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
$(window).on('click', function (e) {
if (e.button === 1 || e.which === 2) {
console.log('middle button');
}
});
You can listen to scroll event on the element being scrolled.
Clicking scroll button will any way trigger scroll event.

Making the scrolling keys scroll the proper page element

I have a DIV in my page that is scrollable, while no other elements on the page are. (The page layout is fixed with controls above and below the DIV itself.) I would like the arrow keys and page up/page down to scroll the DIV under all circumstances, but I cannot seem to do so unless the DIV actually has the focus. There are other input fields in other DIVs which often have focus. I have tried capturing the arrow keys and page/up down 'keydown' event at the document level and simulating the same event directly (using answers from this question) to the DIV that needs to scroll, but no scrolling occurs. I know the event is being dispatched because if I attach an event handler I see it, but for some reason it doesn't cause any scrolling. I have also tried setting the "tabIndex" attribute of the DIV with no difference.
How can I designate a specific element to receive specific keys like this? It is extremely user unfriendly to require a specific element to be focused for certain keys to work, when those keys only make sense for a single element on the page. Having to constantly switch focus from another element to the scrollable area to scroll and back to enter data just isn't acceptable.
I have seen suggestions that scrolling can be simulated by other means, but I want to avoid that route because this doesn't always produce identical results, and I also want to generalize it to other kinds of key events and action besides scrolling.
You can scroll any element by adjusting its scrollTop DOM property.
If you capture all the keydown events on the document and then decide on what action you want to take depending on the key pressed (use the which property of the event object) and maybe some other circumstances (inputs focused, controls checked etc.) you can easily scroll your div. Check out this fiddle for a simple demo.
you can you keypress or keyDown events on the document and trigger actions on your DIV.
$(document).ready(function(){
$(document).on("keydown", handleKeyDown);
function handleKeyDown(evt) {
var code = parseInt(evt.keyCode); // the key Code of the key which was pressed during the event /and parses it and returns a 'integer'.
if(code == 38){ // 38 is the keycode for UP key on the keyboard
alert("up");
} else if(code == 40) // 40 is the keycode for down key on the keyboard.
alert("down");
}
}
});
and as explained in breif by Tadeáš Peták you can use the scrollTop DOM property to scroll any element, enjoy.

Prevent context menu and mouse drag on all elements but one

I am trying to prevent right click context menu, and mouse drag on every element on my page except for one called 'IPAddress'.
Using the code below would seem to do the job, but I still cannot select the element 'IPAddress'.
How can this be altered to allow for this behavior?
html.on('selectstart dragstart contextmenu', function (evt) { // prevent right click, and mouse drag
if (html.not('#IPAddress')) {
evt.preventDefault(); return false;
};
});
Try this:
if (!$(evt.target).is('#IPAddress'))
jQuery not is intended for elements-set filtering, and is not the opposite of is.

Detect when mouse actually moves, not just when the page moves

I'm having a pretty big problem trying to create navigation on my page. If the mouse enters an element then it selects it, then if you use arrow keys it will select the elements relative to the selected one. However this is an issue when the arrow keys cause the page to scroll, because (depending on the position of the mouse) it will select the appropriate element then instantly select the item the mouse is now over after the page moved (even if you didn't move the mouse).
Does anyone know how to fix this problem? I tried tinkering with it but none of my solutions seemed to work. Any help is appreciated, thank you.
It sounds like you should bind the "select when mouse enters" event on mousemove and unbind said event on mousestop. mousestop does not exist on its own, so you will have to create it somehow or use a plugin (there are at least a few out there such as https://github.com/richardscarrott/jquery-mousestop-event/ ). I think this would be the simplest solution, but your UI seems a little bizarre (you want the arrow key to scroll the page normally and "select" an element that's possibly larger than the scroll size?)
Not sure I completely understand, but you should be able to use a combination of the mousemove and keypress events:
$("#element").mousemove(function(e){
alert("mouse moved");
});
$("#element").keypress(function(e){
if (e.keyCode == 38 || e.keyCode == 40){ //up & down arrow keys
e.preventDefault();
}
});
Try returning false from the keyboard event handler where you check for arrow keys:
element.onkeypress = function(ev) {
// ...
return false;
}
This will prevent the "default behavior" that the browser has for the event, which is to scroll. This works also for links, for example: if you return false from a click event handler for a link, clicking the link will not automatically follow it.

Categories