Stop keyboard event bubbling to parent - javascript

We have a pill which contains label and a button. After selecting the pill, we can remove it on Delete or Backspace key. Also the same can be deleted on button click within the pill.
Question: When I focus on the button within the pill and press either Delete or Backspace then the event on the parent is been triggered(onKeyPress function is been called). The issue I am facing is how to stop these keyboard events (Delete & Backspace) from been called when we are on button.
Tried, preventDefault and stopPropagation. Nothing seems to be working.
<span class="pill" tabindex="0">
<span>Pill<span>
<button type="button" class="btn">X</button>
</span>
function onKeyPress(e) {
switch (e.keyCode) {
case 46:
case 8:
e.preventDefault();
handleClose();
break;
default:
break;
}
}
function handleClose() {
console.log('Clear the pill!');
}
document.querySelector('.pill')
.addEventListener('keydown', onKeyPress);
document.querySelector('.btn')
.addEventListener('click', handleClose);

You can use stopPropagation like this. The event is bubbling up from button to its parent the span. You can check for key codes and stop propagation accordingly.
//Check for your specific keycodes here:
function clickButton(e){
if(e.keyCode == 46 || e.keyCode == 8)
e.stopPropagation();
}
document.querySelector('.btn')
.addEventListener('keydown', clickButton);
You can remove the check but that will not trigger even the Enter and Tab key.

Add tabindex="-1" to the nested <button> element (you will likely want this, anyway), then handle/override focus that results from clicks. If I were building this, I would prefer focus be placed on the overall pill if I clicked anywhere on/within it, so that's what I'm demonstrating in the snippet below:
function onKeyPress(e) {
switch (e.keyCode) {
case 46:
case 8:
console.log(`${e.target.className} is in focus`);
handleClose(e);
break;
default: break;
}
}
function handleClose(e) {
console.log('Clear the pill!');
}
// Re-places focus after processing clicks on nested elements
const pillElement = document.querySelector('.pill');
pillElement.addEventListener('click', () => { pillElement.focus(); });
pillElement
.addEventListener('keydown', onKeyPress);
document.querySelector('.btn')
.addEventListener('click', handleClose);
.pill {
border: .25em solid black;
padding: .25em;
border-radius: 1em;
background-color: black;
color: white;
font-family: sans-serif;
}
.pill:focus,
.pill:active {
border-color: blue;
outline: none;
}
.pill > .btn {
border: 0;
background-color: transparent;
color: inherit;
font-family: inherit;
cursor: pointer;
border-radius: 50%;
line-height: 1.5em;
vertical-align: middle;
}
.pill > .btn:hover {
background-color: #333;
}
<span class="pill" tabindex="0">
<span>Pill</span>
<button type="button" class="btn" tabindex="-1">✕</button>
</span>

Related

Why does a mouse right click fire the "pointerdown" event when pressed down but not the "pointerup" event when released?

I'm using pointerevents rather than mouse events to be able to have a universal pointer solution, rather than having to separately consider touch vs mouse events etc.
The problem is that when I press the right mouse button, the pointerdown event is fired normally (as expected), but when releasing it, the pointerup event is not fired (unexpectedly).
I've created a minimum reproducible case here:
https://codesandbox.io/s/proud-smoke-1x2w5?file=/src/index.js
And I've created a video of the issue here:
https://app.usebubbles.com/6a21646e-13d2-4a7f-a598-dfad35a9c0d3
Why does a mouse right click fire the "pointerdown" event when pressed down but not the "pointerup" event when released?
Note that this is in Chrome 81 (https://www.whatsmybrowser.org/b/VJUHP)
It appears to have to do with the "contextmenu", check the snippet below, if you prevent the default behaviour then the "pointerup" event is triggered on the right click.
const app = document.getElementById("app");
const count = document.getElementById("count");
const writeCount = (n) => (count.innerHTML = n);
noContextMenu.addEventListener("contextmenu", (e) => {
e.preventDefault();
});
let n = 0;
writeCount(0);
app.addEventListener("pointerdown", (e) => {
writeCount(++n);
eventType.innerHTML = "pointerdown";
});
app.addEventListener("pointerup", (e) => {
writeCount(--n);
eventType.innerHTML = "pointerup";
});
body {
font-family: sans-serif;
}
#contextmenu {
margin: 10px;
padding: 20px;
width: 100px;
float: left;
border: 1px solid blue;
}
#noContextMenu {
margin: 10px;
padding: 20px;
width: 100px;
float: left;
border: 1px solid darkviolet;
}
<div id="app">
<div>
Event type: (<span id="count"></span>) <span id="eventType"></span>
</div>
<div id="contextmenu">context menu</div>
<div id="noContextMenu">noContextMenu</div>
</div>

onmouseover not working in option [duplicate]

I am trying to show a description when hovering over an option in a select list, however, I am having trouble getting the code to recognize when hovering.
Relevant code:
Select chunk of form:
<select name="optionList" id="optionList" onclick="rankFeatures(false)" size="5"></select>
<select name="ranks" id="ranks" size="5"></select>
Manipulating selects (arrays defined earlier):
function rankFeatures(create) {
var $optionList = $("#optionList");
var $ranks = $("#ranks");
if(create == true) {
for(i=0; i<5; i++){
$optionList.append(features[i]);
};
}
else {
var index = $optionList.val();
$('#optionList option:selected').remove();
$ranks.append(features[index]);
};
}
This all works. It all falls apart when I try to deal with hovering over options:
$(document).ready(
function (event) {
$('select').hover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
alert('yeah!');
};
})
})
I found that code while searching through Stack Exchange, yet I am having no luck getting it to work. The alert occurs when I click on an option. If I don't move the mouse and close the alert by hitting enter, it goes away. If I close out with the mouse a second alert window pops up. Just moving the mouse around the select occasionally results in an alert box popping up.
I have tried targeting the options directly, but have had little success with that. How do I get the alert to pop up if I hover over an option?
You can use the mouseenter event.
And you do not have to use all this code to check if the element is an option.
Just use the .on() syntax to delegate to the select element.
$(document).ready(function(event) {
$('select').on('mouseenter','option',function(e) {
alert('yeah');
// this refers to the option so you can do this.value if you need..
});
});
Demo at http://jsfiddle.net/AjfE8/
try with mouseover. Its working for me. Hover also working only when the focus comes out from the optionlist(like mouseout).
function (event) {
$('select').mouseover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
alert('yeah!');
};
})
})
You don't need to rap in in a function, I could never get it to work this way. When taking it out works perfect. Also used mouseover because hover is ran when leaving the target.
$('option').mouseover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
console.log('yeah!');
};
})​
Fiddle to see it working. Changed it to console so you don't get spammed with alerts. http://jsfiddle.net/HMDqb/
That you want is to detect hover event on option element, not on select:
$(document).ready(
function (event) {
$('#optionList option').hover(function(e) {
console.log(e.target);
});
})​
I have the same issue, but none of the solutions are working.
$("select").on('mouseenter','option',function(e) {
$("#show-me").show();
});
$("select").on('mouseleave','option',function(e) {
$("#show-me").hide();
});
$("option").mouseover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
alert('yeah!');
};
});
Here my jsfiddle https://jsfiddle.net/ajg99wsm/
I would recommend to go for a customized variant if you like to ease
capture hover events
change hover color
same behavior for "drop down" and "all items" view
plus you can have
resizeable list
individual switching between single selection and multiple selection mode
more individual css-ing
multiple lines for option items
Just have a look to the sample attached.
$(document).ready(function() {
$('.custopt').addClass('liunsel');
$(".custopt, .custcont").on("mouseover", function(e) {
if ($(this).attr("id") == "crnk") {
$("#ranks").css("display", "block")
} else {
$(this).addClass("lihover");
}
})
$(".custopt, .custcont").on("mouseout", function(e) {
if ($(this).attr("id") == "crnk") {
$("#ranks").css("display", "none")
} else {
$(this).removeClass("lihover");
}
})
$(".custopt").on("click", function(e) {
$(".custopt").removeClass("lihover");
if ($("#btsm").val() == "ssm") {
//single select mode
$(".custopt").removeClass("lisel");
$(".custopt").addClass("liunsel");
$(this).removeClass("liunsel");
$(this).addClass("lisel");
} else if ($("#btsm").val() == "msm") {
//multiple select mode
if ($(this).is(".lisel")) {
$(this).addClass("liunsel");
$(this).removeClass("lisel");
} else {
$(this).addClass("lisel");
$(this).removeClass("liunsel");
}
}
updCustHead();
});
$(".custbtn").on("click", function() {
if ($(this).val() == "ssm") {
$(this).val("msm");
$(this).text("switch to single-select mode")
} else {
$(this).val("ssm");
$(this).text("switch to multi-select mode")
$(".custopt").removeClass("lisel");
$(".custopt").addClass("liunsel");
}
updCustHead();
});
function updCustHead() {
if ($("#btsm").val() == "ssm") {
if ($(".lisel").length <= 0) {
$("#hrnk").text("current selected option");
} else {
$("#hrnk").text($(".lisel").text());
}
} else {
var numopt = +$(".lisel").length,
allopt = $(".custopt").length;
$("#hrnk").text(numopt + " of " + allopt + " selected option" + (allopt > 1 || numopt === 0 ? 's' : ''));
}
}
});
body {
text-align: center;
}
.lisel {
background-color: yellow;
}
.liunsel {
background-color: lightgray;
}
.lihover {
background-color: coral;
}
.custopt {
margin: .2em 0 .2em 0;
padding: .1em .3em .1em .3em;
text-align: left;
font-size: .7em;
border-radius: .4em;
}
.custlist,
.custhead {
width: 100%;
text-align: left;
padding: .1em;
border: LightSeaGreen solid .2em;
border-radius: .4em;
height: 4em;
overflow-y: auto;
resize: vertical;
user-select: none;
}
.custlist {
display: none;
cursor: pointer;
}
.custhead {
resize: none;
height: 2.2em;
font-size: .7em;
padding: .1em .4em .1em .4em;
margin-bottom: -.2em;
width: 95%;
}
.custcont {
width: 7em;
padding: .5em 1em .6em .5em;
/* border: blue solid .2em; */
margin: 1em auto 1em auto;
}
.custbtn {
font-size: .7em;
width: 105%;
}
h3 {
margin: 1em 0 .5em .3em;
font-weight: bold;
font-size: 1em;
}
ul {
margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>
customized selectable, hoverable resizeable dropdown with multi-line, single-selection and multiple-selection support
</h3>
<div id="crnk" class="custcont">
<div>
<button id="btsm" class="custbtn" value="ssm">switch to multi-select mode</button>
</div>
<div id="hrnk" class="custhead">
current selected option
</div>
<ul id="ranks" class="custlist">
<li class="custopt">option one</li>
<li class="custopt">option two</li>
<li class="custopt">another third long option</li>
<li class="custopt">another fourth long option</li>
</ul>
</div>

How to have a class toggle function to change and unchange a CSS button styling on click?

I am trying to write a code that toggles the styling of a button. At first, I thought I could do this with CSS and the active selector. But I also want to be able to undo the changes on click, which the active selector does not. Then I thought I might be able to do this with JavaScript and a class toggle function.
Unfortunately, I am not that familiar with JavaScript. I have written a code and it partially works, but it always changes the styling of the same element (maybe because it is the first element with the specific class?).
Is there a way to build a flexible function which can apply to several elements, depending on which one I click on?
function testtoggle() {
if(document.getElementById("testknop").className == "nietactief"){
document.getElementById("testknop").className = "actief";
} else {
document.getElementById("testknop").className = "nietactief";
}
}
button {
cursor: pointer;
padding: 16px;
font-size: 16px;
border-radius: 100%;
}
.nietactief {
background-color: #a8a8a8;
border: 5px solid #ddd;
}
.actief {
background-color: purple;
border: 5px solid green;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button type="button" id="testknop" class="nietactief" onclick="testtoggle()()">1e</button>
<button type="button" id="testknop" class="nietactief" onclick="testtoggle()">2e</button>
<button type="button" id="testknop" class="nietactief" onclick="testtoggle()()">3e</button>
</body>
</html>
You should look into the classList feature in JavaScript. It makes it easy to add, remove and toggle classes on elements.
// Get a list of all the buttons in the group
let buttons = Array.from(document.querySelectorAll('[type=button]'))
// For each button add a click event listener
// This allows us to remove the 'onclick' from the html
buttons.forEach(item => {
item.addEventListener('click', e => {
// When the button is clicked, remove the active state
// Then add the inactive state
buttons.forEach(button => {
// If the current button does not equal the clicked button
// Remove the active state class
button != e.currentTarget && button.classList.remove("actief")
button.classList.add("nietactief")
})
// Toggle the state of the clicked button
item.classList.toggle("actief")
})
})
button {
cursor: pointer;
padding: 16px;
font-size: 16px;
border-radius: 100%;
}
.nietactief {
background-color: #a8a8a8;
border: 5px solid #ddd;
}
.actief {
background-color: purple;
border: 5px solid green;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button type="button" id="testknop" class="nietactief">1e</button>
<button type="button" id="testknop" class="nietactief">2e</button>
<button type="button" id="testknop" class="nietactief">3e</button>
</body>
</html>
What you are observing is the expected behavior to the document.getElementById
You could pass a reference to the button triggering the event:
window.testtoggle = function(elmnt) {
if(elmnt.className == "nietactief"){
elmnt.className = "actief";
} else {
elmnt.className = "nietactief";
}
}
button {
cursor: pointer;
padding: 16px;
font-size: 16px;
border-radius: 100%;
}
.nietactief {
background-color: #a8a8a8;
border: 5px solid #ddd;
}
.actief {
background-color: purple;
border: 5px solid green;
}
<button type="button" id="testknop" class="nietactief" onclick="testtoggle(this)">1e</button>
<button type="button" id="testknop" class="nietactief" onclick="testtoggle(this)">2e</button>
<button type="button" id="testknop" class="nietactief" onclick="testtoggle(this)">3e</button>
Alternatively you could consider JQuery to fetch all dom elements matching a specific criteria and update its class:
$("button").attr('class', 'newClass');
or another client framework like vue.js

"Add to favourites" button to change class and innerHTML

I need to make an "Add to favs" button that would toggle between "Add" and "Added" states. The trick is that when in "Added" state, I want it to have a hover behaviour like all red and saying "Remove from favs". BUT when you click the button for the 1st time and it changes to "Added", I don't want the 'remove' style turn on immediately.
My solution is to create 2 classes: .isChecked and .isJustPressed. The first is used to determine the button actual state and the second is used to apply the "remove from favs" hover styling only after the mouseleave event.
These classes are handled by jQuery. I am quite new to the language, so I've come up with such working solution (see below). The CSS is simplified. Well the reason I posted this is that I feel there must be a more elegant way to solve this. And besides, I don't like my jQuery code, so I'd appreciate any comments there also
$('.fav_btn').click(function(event) {
$(this).addClass('isJustPressed');
$(this).toggleClass('isChecked');
$(this).html(function() {
return $('.fav_btn').hasClass('isChecked') ? 'Added' : 'Add to favourites';
});
});
$('.fav_btn').on('mouseleave', function(event) {
if ( $(this).hasClass('isChecked')){
$('.fav_btn').removeClass('isJustPressed');
}
});
$('.fav_btn').hover(
function() {
if ($('.fav_btn').hasClass('isChecked') && !$('.fav_btn').hasClass('isJustPressed')){
$('.fav_btn').html('Remove from favourites');
}},
function(){
if ($('.fav_btn').hasClass('isChecked') && !$('.fav_btn').hasClass('isJustPressed')){
$('.fav_btn').html('Added');
}});
.fav_btn {
position: relative;
box-sizing: border-box;
width: 100px;
height: 100px;
border: 1px solid black;
background-color: lightblue;
}
.fav_btn:hover{
background-color: blue;
color: #fff;
}
.fav_btn.fav_btn.isChecked.isJustPressed:hover{
background-color: blue;
color: #fff;
}
.fav_btn.isChecked {
background-color: #fff;
}
.fav_btn.isChecked:hover{
background: pink;
color: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="fav_btn">Add to fav</div>
</body>
I'm not sure if this is what you are looking for but, I rewrote your solution with more method chaining, and abstraction:
HTML:
<div class="fav_btn">Add To Favs</div>
JS:
//Moved btn text into variable so it can be changed more easily
var btn_text = {
default: "Add To Favs",
added: "Selection Added",
remove: "Remove Selection"
}
$('.fav_btn')
//set initial text and classes
.removeClass('remove added')
.html(btn_text.default)
//The click toggles the 'default' and 'added' states
.on('click', function(e) {
//Toogle the 'added' class
$(this).toggleClass('added');
//Swap the text
if ($(this).is('.added')) {
$(this).html(btn_text.added);
} else {
$(this)
.removeClass('remove added')
.html(btn_text.default)
}
})
.on('mouseover', function(){
//If it has the 'added' class...add the 'remove' text
if ($(this).is('.added')) {
$(this)
.addClass('remove')
.html(btn_text.remove)
.on('mouseout', function() {
//Get rid of the 'remove' class
$(this).removeClass('remove');
//Swap out the 'remove' text
if ($(this).is('.added')) {
$(this).html(btn_text.added);
} else {
$(this).html(btn_text.default);
}
});
}
});
CSS:
.fav_btn {
padding: 5px;
background-color:blue;
color: white;
}
.fav_btn:hover {
background-color: lightblue;
}
.fav_btn.remove:hover {
background-color: pink;
}

Make it so 'Go/Enter' key is pressable

I have a basic form which makes it so the user cannot leave the input field empty before the form posts the page. It also prevents the user from entering gibberish and requires them to only enter numbers, but this also blocks all keys that aren't numbers including the Go/Enter on mobile keyboards. My question is, is there a way to make it so that the user has to enter only numbers, but also be able to press Go after they have entered the field?
FIDDLE:http://jsfiddle.net/schermerb/nX8Hx/
Currently a user has to input a zip THEN tap back on the screen and THEN click submit.
$(document).ready(function () {
$("#quantity").keypress(function (e) {
if (e.which != 8 && e.which != 0 && (e.which < 48 || e.which > 57)) {
$("#errmsg").html("Enter Valid Zip!").show().fadeOut("5000");
return false;
}
});
});
var initVal = "Have a good name for it? Enter Here";
$(document).ready(function () {
$(".submit-name").attr("disabled", "true");
$(".recipe-name").blur(function () {
if ($(this).val() != initVal && $(this).val() != "") {
$(".submit-name").removeAttr("disabled");
} else {
$(".submit-name").attr("disabled", "true");
}
});
});
input {
width: 100%;
background: #FFFFFF;
border-radius: 4px;
border: 2px solid #acd50b;
padding: 10px 15px;
text-align: center;
-webkit-appearance: none;
color: #999999;
font-size: 11px;
}
input[type="focus"] {
outline: none;
}
input[type="submit"] {
border-radius: 4px;
border: 2px solid #2d8b1b;
-webkit-appearance: none;
background: #acd50b;
padding: 6px 10px;
color: #444444;
width: 100%;
font-size: 18px;
cursor:pointer;
}
.box {
width: 85px;
}
.boxtwo {
width: 160px;
}
<form action="google.html" method="get" id="recipename">
<div class="box">
<input onFocus="this.value=''" type="text" value="Enter Your Zip" placeholder="Enter Your Zip" /><span id="errmsg"></span>
</div>
<div class="boxtwo">
<input type="submit" value="Compare" id="register" disabled value="Compare" class="submit-name" />
</div>
</form>
It would be better to use keydown and test the resulting value and revert it rather than only allowing certain characters to be inserted.
$("#quantity").keydown(function (e) {
var self = this;
var tempVal = this.value;
setTimeout(function () {
if (!isValidZip(self.value)) {
self.value = tempVal;
}
}, 0);
});
function isValidZip(zip) {
return /^[0-9]{1,5}$/.test(zip);
}
http://jsfiddle.net/nX8Hx/3/
This could also be done with keyup or keypress, however keydown makes it happen much quicker and allows you to easily get the previous value.
Doing it this way avoids the issue of preventing the done key by not testing which key is pressed. It's also easily expandable by simply changing the regexp to match a different kind of zip code.

Categories

Resources