I'm trying to prevent page scrolling on my custom dropdown search using StimulusJS. Much like in gmail, where you can type something in a search box and use arrow keys to navigate. I go to that point where I can navigate the dropdown, but at the same time default behaviour for arrow keys is problematic here.
// results are divs that get focused
const results = [node1, node2, node3]
if (this.listCounter <= -1) { this.listCounter = -1 }
if (this.listCounter >= results.length) { this.listCounter = results.length - 1 }
switch (event.key) {
case 'ArrowDown':
this.listCounter++
break
case 'ArrowUp':
this.listCounter--
// when we reach the top we focus back on input element
if (this.listCounter <= 0) {
this.userInputTarget.focus()
}
break
default:
break
}
if (results[this.listCounter]) {
results[this.listCounter].focus()
}
So this works well, but the problem is that pressing arrow keys up/down also invokes scroll on the page. So I tried disabling it, but only when the key is pressed. I don't want to disable this behaviour for the whole page, only when certain elements are focused. Below console.log() gets fired, but it doesn't stop the window from being scrolled.
connect() {
window.addEventListener('keyup', this.preventKeyboardEvents.bind(this), false)
}
preventKeyboardEvents(e) {
const key = e.key
const results = window.allMultisearchActiveElements
const activeElement = results.includes(document.activeElement) || document.activeElement === this.userInputTarget
if (activeElement && (key === "ArrowDown" || key === "ArrowUp" || key === "Enter")) {
e.preventDefault()
console.log('fired')
}
}
Which part of it am I getting wrong? Is it even possible to .preventDefault() only for certain events?
Comes out it was rather simple.
window.addEventListener('keydown', this.preventKeyboardEvents.bind(this), false)
Since when pressing a key keydown event is fired first, I assume it fires a default behaviour of scrolling in the browser window. So using keyup was a problem here, because it was fired after the window has received the event from keydown.
Related
I'm making text editor.
This is demo image what i made
Using contenteditable, I render code to dangerouslySetInnerHTML.
So the component look like this
But like figure1, I can access next to select
( In second figure, it is between </div> here <select ~~>
I want to prevent a user from accessing and writing at that point
But I did not found preventing access method.
So I thought when user write content then check the parent and execute event.preventDefault() except for left arrow and up arrow.
it works well in English and others.
But when I write Korean. PreventDefault is not working.
How can I execute preventDefault in Korean??
handleKeyDown = (event) => {
let key;
if(window.event) {
key = event.keyCode;
} else {
key = event.which; //For Firefox
}
const selection = document.getSelection();
if (selection.anchorNode) {
const check = selection.anchorNode.parentElement;
if (check.className.includes('src-components') && key !==37 && key !== 38) {
event.preventDefault();
event.stopPropagation();
}
}
}
P.S : the event.target.value return undefined.
I have a filter container with three tabs. The currently selected tab has class ag-tab-selected. When the container loads, the first tab is always selected and has the ag-tab-selected class. I need to be able to navigate between the tabs with the left and right arrow keys.
As the user hits the right arrow key, the ag-tab-selected class should be applied to the next tab, and if the left arrow key is hit, the ag-tab-selected class should be applied to the previous tab. The current tab should also have .click() applied to it so that when the user does hit the arrow key, the view is changed based on which tab is selected. I'm currently able to loop through the available tabs and have a trigger applied to the current one, but having trouble iterating to the previous or next with the arrow keys:
if(e.key === 'ArrowRight') {
for(let i = 0; i < tabTriggers.length; i++){
//on arrow, trigger currently focused ag-tab with click
if(tabTriggers[0].classList.contains('ag-tab-selected') === true){
tabTriggers[i].click();
}
}
}
Link to current fiddle: Link
I finished the task based on the code you provided, i also extended it with some logic so that you'd have a working example. It's available here http://jsfiddle.net/631zjmcq/
So I defined a click handler for each tab, so that you gain some interactivity (i wanted to see the solution in action)
The basis of the code you provided was working well, it just had the problem that once you've found an element, you didn't stop the loop and it ran until it reached the last element in the list. This means it clicked every element until it reached the last one, so I put a break statement into that for loop.
To go backwards, I modified the for loop, so that this time it would loop from the end of the list backwards.
const selectedTabClass = 'ag-tab-selected';
document.querySelectorAll('.ag-tab').forEach(tab => {
tab.addEventListener('click', e => {
if (tab.classList.contains('ag-tab-selected')) return
document.querySelector(`.${selectedTabClass}`)
.classList.remove(selectedTabClass)
tab.classList.add(selectedTabClass)
})
})
document.addEventListener('keydown', e => {
let tabTriggers = document.querySelectorAll('.ag-tab');
if (e.key === 'ArrowRight') {
for (let i = 0; i < tabTriggers.length; i++) {
if (tabTriggers[i].classList.contains('ag-tab-selected') === true) {
tabTriggers[i+1].click();
break;
}
}
}
if (e.key === 'ArrowLeft') {
for (let i = tabTriggers.length-1; i > 0; i--) {
if (tabTriggers[i].classList.contains('ag-tab-selected') === true) {
tabTriggers[i-1].click();
break;
}
}
}
});
I'm trying to let the user check/uncheck the checkbox on hitting either spacebar or enter in keyboard, I want to achieve this functionality using JavaScript function.
This is how my code looks partially:
<span class="sample" onClick="UdateComponent" tabindex="0" role="checkbox" aria-checked="" aria-decribedby="">
Inside this span I want to include onkeypress or onkeydown for achieving the functionality that is mentioned above and the constraint is I only have to use JavaScript for this.
I would strongly recommend not doing this. Use an input type="checkbox", in combination with a label. It's what they're for. You can style them extremely thoroughly. You can even hide the input type="checkbox" if you want to and only show the label.
But you've said you can't use input. So yes, you can do this with a keypress handler. You'll presumably also want to handle clicks. See comments:
// Handle toggline the "checkbox"
// Expects the element as `this` and the event as `e`
function toggleFakeCheckbox(e) {
// States as far as I can tell from
// https://www.w3.org/TR/wai-aria/states_and_properties#aria-checked
// and
// https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_checkbox_role
this.setAttribute(
"aria-checked",
this.getAttribute("aria-checked") === "true" ? "false" : "true"
);
// Avoid the default (spacebar in particular is problematic)
e.preventDefault();
e.stopPropagation();
}
// Get the element
var sample = document.querySelector(".sample");
// Set up its handlers for click and keypress
sample.addEventListener("click", toggleFakeCheckbox);
sample.addEventListener("keypress", function(e) {
// Flag for whether to toggle
var toggle = false;
var keyCode;
if ("key" in e) {
// Modern user agent
toggle = e.key === " " || e.key === "Enter";
} else {
// Fallback for older user agents
keyCode = e.which || e.keyCode;
toggle = keyCode === 32 || keyCode === 13;
}
if (toggle) {
toggleFakeCheckbox.call(this, e);
}
});
// Give it focus for easy testing
sample.focus();
/* Let's show the state of the checkbox */
[role=checkbox][aria-checked=true]:before {
content: '[x] '
}
[role=checkbox][aria-checked=false]:before {
content: '[ ] '
}
<span class="sample" tabindex="0" role="checkbox" aria-checked="true" aria-decribedby="">Checkbox</span>
But again: Reinventing the wheel isn't a good thing, even if you try to respect all the ARIA rules when doing so...
Update: And sure enough, focussing the span in IE and hitting the space bar moves us to a different part of the page, even though we both prevented the default action (which was enough to stop that on Firefox) and stopped propagation. Why does it do that? Because we tried to reinvent the wheel. Which is a Bad Thing™.
on down arrow keypress , click event is getting fired, event.keycode is undefined
$(".dropdown:not(.li-search) a.dropdown-toggle", ".navbar-collapse").on("click", function(event) {
var target = $(this).attr("target");
if (event.keyCode !== '40'){
if (!$(".li-menu").is(":visible") && target === undefined) {
location.href=this.href;
} else {
window.open(this.href, '_blank');
}
}
});
in this code i am trying to open main menu in new tab , but external link is getting open on down arrow keypress
call preventDefault() function.
$(".dropdown:not(.li-search) a.dropdown-toggle", ".navbar-collapse").on("click", function(event) {
event.preventDefault();
var target = $(this).attr("target");
if(event.keyCode!=='40'){
if (!$(".li-menu").is(":visible") && target===undefined) {
location.href=this.href;
}
else {
window.open(this.href,'_blank');
}
}
});
See the keycode for the reference https://css-tricks.com/snippets/javascript/javascript-keycodes/
in order to configure your app for particular key event
Looking at the classes dropdown-toggle, navbar-collapse, I'm guessing that you are using Bootstrap library.
If that is the case, the behaviour you are seeing is reasonable. Let's break down the issues:
on down arrow keypress , click event is getting fired
Q: You have only bind the handler on click event so why are it is being triggered on keypress?
A: Because this is a feature of bootstrap dropdown. To have better accessibilty, bootstrap triggers click event on the keydown of up, down, esc and space keys.
event.keycode is undefined
Since it is a click event handler and not some keyboard event handler like keydown or keypress, event.keyCode should be undefined
Note: You are using a strict equality in the following condition
if (event.keyCode !== '40')
This will check both the type and value of the operands. Now, event.keyCode always return a Number while '40' is a string, hence the above condtion will yield false even if keyCode is 40. You should correct it to:
if (event.keyCode !== 40)
Now, if you want to stop the redirect on down key, you should check whether the event triggered is an original event or was triggered by some js logic. For this, you may choose jQuery's event.isTrigger or event.originalEvent
Here's a code snippet:
$(".dropdown:not(.li-search) a.dropdown-toggle", ".navbar-collapse").on("click", function(event) {
var target = $(this).attr("target");
// Check if NOT an triggered event
if (!event.isTrigger) {
if (!$(".li-menu").is(":visible") && target === undefined) {
location.href = this.href;
} else {
window.open(this.href, '_blank');
}
}
});
<a> tags will fire the click event when you press enter on them. However you will not have a keyCode on the event because it is not a Key* event. If you want to know the keyCode add a keyDown or keyUp handler as well. You could also handle both by doing something like the following:
$(".dropdown:not(.li-search) a.dropdown-toggle", ".navbar-collapse").on("click keydown", function(event) {
var target = $(this).attr("target");
if(event.type === 'keydown' && event.keyCode!=='40'){
if (!$(".li-menu").is(":visible") && target===undefined) {
location.href=this.href;
}
else {
window.open(this.href,'_blank');
}
}
});
You'll probably also want to add an event.preventDefault(); in there if you wish to prevent default browser behaviour from taking place.
How can I clarify ALT+CTRL and ALTGR key press?
I found this code here as possible solution, but it's doesn't work:
if (event.ctrlKey && event.altKey) {
}
This code is true for alt+ctr and for altGr as well.
I have situation like this: for alt+ctrl+e (for example e, it's no matter) I want one thing and for altGr+e another, how can I do this?
If anyone have some idea, please tell me.
You can detect which key is pressed (from right key or left key) by value of location property in event object. If value of location property is 1 (e.location=1) then left key is pressed. if value is 2 then right key is pressed.
Here I have providing my code for RightAlter+RightCtrl+<any_valid_key>
Check this Example
var isRightAltKey=false;
var isRightCtrlKey=false;
var validKeys=['a','s','d','f','g']; //keep empty array if no need to check key
document.addEventListener("keydown", function(e) {
if(e.key=="Alt"){
// when right Alter pressed make isRightAltKey true
isRightAltKey= (e.location==2);
}
else if(e.key=="Control"){
// when right Control pressed make isRightCtrlKey true,
//if you need any ctrl key pressed then simply set isRightCtrlKey= true;
isRightCtrlKey= (e.location==2);
}
// checking both right key is pressed already or not?
var isRightKeys= isRightAltKey && isRightCtrlKey;
// validate other keys [optional]
var isValidKey=((typeof validKeys === "undefined") || validKeys.length==0 || validKeys.indexOf(e.key.toLowerCase())>=0);
if (isRightKeys && isValidKey){
document.getElementById("detect_key").innerHTML = "RightAlt + RightCtrl + "+e.key;
}
else
{
document.getElementById("detect_key").innerHTML="";
}
}, false);
document.addEventListener("keyup", function(e) {
if(e.key=="Alt"){
// when right Alter released make isRightAltKey false
isRightAltKey= false;
}
else if(e.key=="Control"){
// when right Control released make isRightCtrlKey false
isRightCtrlKey= false;
}
}, false);
<div id="detect_key"></div>
Why attached keyup event listner?
Here we have to detect key location when Ctrl and Alt key is pressed (on keydown event). and we have to store it in flag variable and make it true. when key is released (on keyup event) have to mark as false. Otherwise those flags always remain true. on Next key press it will always true
You can use the location to determined which alt is being pressed.
In order to support Alt+Ctrl we'll save the last location of the pressed Alt.
Location = 1 // Left
Location = 2 // Right
Then, once both Alt and Ctrl are pressed, do your thing. In this example, we'll just write the Alt side in the result div. You can add the "e" pressed condition as well:
if (e.ctrlKey && e.altKey && e.key == "e"){
Example
HTML
<div class="cont">
Click Alt + Ctrl<br /><br />
<div id="res"></div>
</div>
Javascript
var lastAltLocation;
document.addEventListener("keydown", function(e) {
if (e.key == "Alt"){
lastAltLocation = e.location;
}
if (e.ctrlKey && e.altKey){
if (lastAltLocation == 1){
document.getElementById("res").innerHTML = "Left";
}
if (lastAltLocation == 2){
document.getElementById("res").innerHTML = "Right";
}
}
}, false);
Sticking strictly to your question here are the codes for both the required cases:
document.addEventListener ("keydown", function (zEvent) {
if (zEvent.altKey && zEvent.code === "KeyE") {
if(zEvent.ctrlKey) {
//Do Ctrl+Alt+E Stuff Here.
} else {
//Do Alt+E Stuff Here.
}
});
Now breaking down the things going on here. keydown allows you to detect multiple keypresses.
First we check if the Alt and E keys are pressed. If yes, we then go on to check in the Ctrl key is also active and take the appropriate action as needed.