Hi have simple radio buttons of primeng, while changing the choise, I have some logic on the onClick function, checking if the user changed some fields, if he changed I will show message if he sure he want to leave the choice of the radio button, if he will press "cancel" I want to cancel all the event of the onlick function and to undo to his last choise. but the event of the onclick not doing it, I checked all the function of java script. I tried now to add HostListener that if some boolean field(the one that said the user want to undo)it will stopImmediatePropagation. but on runtime the onclick function called and not the HostListener. some ideas what to do?
radio button
<p-radioButton name="treesDetailsType" [(ngModel)]="selectedType" formControlName="selectedType" (onClick)="onChangeType(type,$event)" class="treeDetails" value="{{type.id}}" label="{{type.desc}}" [disabled]="isReadOnly && type.id != data.selectedType"></p-radioButton>
the onclick function
onChangeType(type, event) {
let change = this.checkChanges(type, event);
if (change) {
//HERE I WANT TO CANCEL ALL THE CHANGE AND TO LEAVE THE FUNCTION
this.clickDisallowed = true;
}
else {
switch (type.id)
.....
}
}
the host listner
#HostListener('click', ['$event']) onClick(event) {
if (this.clickDisallowed) {
event.stopImmediatePropagation();
}
console.log(event);
}
Maybe you are using the hostlistener wrongly.
In the angular example it is used inside a directive
https://angular.io/api/core/HostListener
Try this example!
#Directive({selector: 'button[counting]'})
class CountClicks {
numberOfClicks = 0;
#HostListener('click', ['$event.target'])
onClick(btn) {
console.log('button', btn, 'number of clicks:', this.numberOfClicks++);
}
}
#Component({
selector: 'app',
template: '<button counting>Increment</button>',
})
class App {}
I'am trying to show / hide a div if the Alt button is pressed. I'm listening to a keypress and using event.altKey to determine if alt/option was pressed. But this wasn't working. So I logged the event.
#HostListener( 'document:keypress', [ '$event' ])
handleKeyboardEvent( event: KeyboardEvent ) {
console.log( event.altKey );
}
In the above code if I pressed any number, alphabet or symbol it will print 'false' as expected. But when I press Alt, Ctrl, Shift, Tab nothing happens. It doesn't print true or false.
I also tried printing the keycode. It prints for everything but 'Alt, Ctrl, Shift, Tab'.
What am I doing wrong here?
Keypress doesn't detect some keys, and alt must be one of those keys. I would try using a different event called onkeydown. It can detect more keys. Here's more info on the keydown event: https://developer.mozilla.org/en-US/docs/Web/Events/keydown
you can the following function for alt key function :
function isKeyPressed(event) {
if (event.altKey) {
console.log("The ALT key was pressed!");
} else {
console.log("The ALT key was NOT pressed!");
}
}
and in your html use onkeydown="isKeyPressed(event)" as tag
You can try host property within #Component decorator.
#Component({
...
host: {
'(document:keypress)': 'handleKeyboardEvent($event)'
}
})
export class AppComponent {
handleKeyboardEvent(event: KeyboardEvent) {
console.log(event);
}
}
Angular recommend using #HostListener decorator over host property
https://angular.io/guide/styleguide#style-06-03
In my application I have a button with a click even on it:
<button class="btn btn-default" (click)="doSomething()">
From within the doSomething method, is there any way to remove the (click) event from the button (so the user can't trigger the functionnality anymore?).
I tried to set a disabled prop on the button but it doesn't change Angular2 behavior.
I tryed to use (click)="doSomething($event) and then
doSomething($event) {
// My method logic goes here
...
...
console.log('Method Logic');
//Attempt to overwrite click event
let target = event.target || event.srcElement || event.currentTarget;
this.renderer.listen(target, 'click', (event) => {
console.log('clic block');
});
}
But It doesn't "replace" the click event. So after that, on click, both original logic and the "click block" console log are triggered!
Method 1:
You can set a boolean variable, so if the user calls the function, boolean value changes and the user will be able to call the function on click again but actually nothing will happen.
bool: boolean = true;
doSomething($event) {
if (this.bool) {
// My method logic goes here
...
...
console.log('Method Logic');
this.bool = false;
}
}
Method 2:
You can add a condition to your html component, if specified variable (in this case bool) is true, the function is going to be executed, but only once because the bool variable will be set to false and the click function will execute nothing (null) from now on.
bool: boolean = true;
doSomething($event) {
// My method logic goes here
...
...
console.log('Method Logic');
this.bool = false;
}
(click)="bool ? doSomething($event) : null"
The downside of just adding a guard variable for the execution is that the actual event listener is still in place and will trigger Angular's change detection when clicked, even though it doesn't do anything.
To actually remove the event listener, you have to add it via the component's Renderer. This will return a function that removes the event listener when called:
import {Component, AfterViewInit, Renderer, ViewChild, ElementRef} from '#angular/core';
#Component({
template: `<button #button>...</button>`
})
export class SampleComponent implements AfterViewInit {
#ViewChild('button') button: ElementRef;
private cancelClick: Function;
constructor(private renderer: Renderer) {}
ngAfterViewInit() {
this.cancelClick = this.renderer.listen(this.button.nativeElement, 'click',
($event: any) => this.handleClick($event));
}
handleClick($event: any) {
this.cancelClick();
// ...
}
}
If your goal is to just remove the event listener when the event is fired for the first time, this can be implemented as a plugin to Angular's event system. I added it as a part of my ng2-events utility library [source], which lets you now do the following:
<button (once.click)="handleClick($event)">...</button>
For people dealing with Observable / Subject for handling some events :
<button (click)="clickEvents$.next($event)">
class MyComponent {
clickEvents$ = new Subject<MouseEvent>();
firstClick$ = this.clickEvents.take(1); // Observable of first click
}
Is it possible to disable the scroll wheel changing the number in an input number field?
I've messed with webkit-specific CSS to remove the spinner but I'd like to get rid of this behavior altogether. I like using type=number since it brings up a nice keyboard on iOS.
Prevent the default behavior of the mousewheel event on input-number elements like suggested by others (calling "blur()" would normally not be the preferred way to do it, because that wouldn't be, what the user wants).
BUT. I would avoid listening for the mousewheel event on all input-number elements all the time and only do it, when the element is in focus (that's when the problem exists). Otherwise the user cannot scroll the page when the mouse pointer is anywhere over a input-number element.
Solution for jQuery:
// disable mousewheel on a input number field when in focus
// (to prevent Chromium browsers change the value when scrolling)
$('form').on('focus', 'input[type=number]', function (e) {
$(this).on('wheel.disableScroll', function (e) {
e.preventDefault()
})
})
$('form').on('blur', 'input[type=number]', function (e) {
$(this).off('wheel.disableScroll')
})
(Delegate focus events to the surrounding form element - to avoid to many event listeners, which are bad for performance.)
One event listener to rule them all
This is similar to #Simon Perepelitsa's answer in pure js, but a bit simpler, as it puts one event listener on the document element for all inputs and checks if the focused element is a number input tpye:
document.addEventListener("wheel", function(event){
if(document.activeElement.type === "number"){
document.activeElement.blur();
}
});
If you want to turn off the value scrolling behaviour on some fields by class/id, but not others just add && and the corresponding document selector instead:
document.addEventListener("wheel", function(event){
if(document.activeElement.type === "number" &&
document.activeElement.classList.contains("noscroll"))
{
document.activeElement.blur();
}
});
with this:
<input type="number" class="noscroll"/>
If an input has the noscroll class it wont change on scroll, otherwise everything stays the same.
Test here with JSFiddle
$(document).on("wheel", "input[type=number]", function (e) {
$(this).blur();
});
You can simply use the HTML onwheel attribute.
This option have no effects on scrolling over other elements of the page.
And add a listener for all inputs don't work in inputs dynamically created posteriorly.
Aditionaly, you can remove the input arrows with CSS.
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
<input type="number" onwheel="this.blur()" />
I have an alternative suggestion. The problem I see with most of the common recommendation of firing a blur event is that it has unexpected side-effects. It's not always a good thing to remove a focus state unexpectedly.
Why not this instead?
<input type="number" onwheel="return false;" />
It's very simple and straight-forward, easy to implement, and no side-effects that I can think of.
input = document.getElementById("the_number_input")
input.addEventListener("mousewheel", function(event){ this.blur() })
http://jsfiddle.net/bQbDm/2/
For jQuery example and a cross-browser solution see related question:
HTML5 event listener for number input scroll - Chrome only
#Semyon Perepelitsa
There is a better solution for this. Blur removes the focus from the input and that is a side affect that you do not want. You should use evt.preventDefault instead. This prevents the default behavior of the input when the user scrolls. Here is the code:
input = document.getElementById("the_number_input")
input.addEventListener("mousewheel", function(evt){ evt.preventDefault(); })
For anyone working with React and looking for solution. I’ve found out that easiest way is to use onWheelCapture prop in Input component like this:
onWheelCapture={e => {
e.target.blur()
}}
ReactJS Solution
For those needing a React solution, here's an onWheel handler for your type="number" input to prevent the number from changing and prevent the page from scrolling while the user tries to wheel over the input. Finally, it'll refocus on the input so the user can keep editing as intended:
const numberInputOnWheelPreventChange = (e) => {
// Prevent the input value change
e.target.blur()
// Prevent the page/container scrolling
e.stopPropagation()
// Refocus immediately, on the next tick (after the current function is done)
setTimeout(() => {
e.target.focus()
}, 0)
}
return <input type="number" onWheel={numberInputOnWheelPreventChange}/>
First you must stop the mousewheel event by either:
Disabling it with mousewheel.disableScroll
Intercepting it with e.preventDefault();
By removing focus from the element el.blur();
The first two approaches both stop the window from scrolling and the last removes focus from the element; both of which are undesirable outcomes.
One workaround is to use el.blur() and refocus the element after a delay:
$('input[type=number]').on('mousewheel', function(){
var el = $(this);
el.blur();
setTimeout(function(){
el.focus();
}, 10);
});
Easiest solution is to add onWheel={ event => event.currentTarget.blur() }} on input itself.
Typescript Variation
Typescript needs to know that you're working with an HTMLElement for type safety, else you'll see lots of Property 'type' does not exist on type 'Element' type of errors.
document.addEventListener("wheel", function(event){
const numberInput = (<HTMLInputElement>document.activeElement);
if (numberInput.type === "number") {
numberInput.blur();
}
});
The provided answers do not work in Firefox (Quantum). The event listener needs to be changed from mousewheel to wheel:
$(':input[type=number]').on('wheel',function(e){ $(this).blur(); });
This code works on Firefox Quantum and Chrome.
If you want a solution that doesn’t need JavaScript, combining some HTML functionality with a CSS pseudo-element does the trick:
span {
position: relative;
display: inline-block; /* Fit around contents */
}
span::after {
content: "";
position: absolute;
top: 0; right: 0; bottom: 0; left: 0; /* Stretch over containing block */
cursor: text; /* restore I-beam cursor */
}
/* Restore context menu while editing */
input:focus {
position: relative;
z-index: 1;
}
<label>How many javascripts can u fit in u mouth?
<span><input type="number" min="0" max="99" value="1"></span>
</label>
This works because clicking on the contents of a <label> that’s associated with a form field will focus the field. However, the “windowpane” of the pseudo-element over the field will block mousewheel events from reaching it.
The drawback is that the up/down spinner buttons no longer work, but you said you had removed those anyway.
In theory, one could restore the context menu without requiring the input to be focused first: :hover styles shouldn’t fire when the user scrolls, since browsers avoid recalculating them during scrolling for performance reasons, but I haven’t thoroughly cross-browser/device tested it.
In my case, I needed to maintain focus and still apply the scroll. None of the solutions above can handle that and doing blur/focus feels a bit hacky to me.
This maintains existing focus and also keeps the scroll. You know... like the browser should. Only minimally tested in chrome and only supports Y-axis.
// you could make this target a specific input instead of document
document.addEventListener('wheel', event => {
if (!event.target) return;
const isNumberInput = event.target.nodeName === 'INPUT' && event.target.type === 'number';
const isFocused = event.target === document.activeElement;
if (isNumberInput && isFocused) {
// prevent stupid input change
event.preventDefault();
// since we're taking over scrolling, we want to make sure
// nothing else gets the event
event.stopPropagation();
// finally we reapply the scroll
applyScroll(event);
}
}, { passive: false });
// this walks up the tree for event.target to find the first
// scrollable parent. this is probably good enough for most situations.
const applyScroll = event => {
try {
// console.debug('attempting to reapply scroll. searching for scrollable container...');
let scrollContainer = event.target;
while (scrollContainer && scrollContainer !== document.body && !elementIsScrollable(scrollContainer)) {
scrollContainer = scrollContainer.parentElement;
// console.debug('\t-> container was not scrollable. checking parent', scrollContainer);
}
if (scrollContainer) {
// console.debug('scrollContainer container found. applying scroll', scrollContainer, event.deltaY);
scrollContainer.scrollBy({ top: event.deltaY });
}
else {
// console.debug('no scrollContainer found');
}
}
catch (err) {
console.info('failed to reapply scroll', err, event);
}
};
const elementIsScrollable = element => {
const { scrollHeight = 0, offsetHeight = 0 } = element;
const scrollable = style.overflowY === 'auto' || style.overflowY === 'scroll';
return scrollable && scrollHeight > 0 && offsetHeight > 0 && element.scrollHeight > element.offsetHeight;
};
Non-JS solution
I like using type=number since it brings up a nice keyboard on iOS.
The keyboard is nice indeed. But we can get the same behaviour with:
<input inputmode="numeric" pattern="[0-9]*" />
Taken from gov.uk which was linked in the MUI docs. Works nicely for our product.
Grain of salt
Please check browser support for inputmode. Most mobile browsers are supported, and to me inputmode is mostly about the mobile experience. But YMMV.
While trying to solve this for myself, I noticed that it's actually possible to retain the scrolling of the page and focus of the input while disabling number changes by attempting to re-fire the caught event on the parent element of the <input type="number"/> on which it was caught, simply like this:
e.target.parentElement.dispatchEvent(e);
However, this causes an error in browser console, and is probably not guaranteed to work everywhere (I only tested on Firefox), since it is intentionally invalid code.
Another solution which works nicely at least on Firefox and Chromium is to temporarily make the <input> element readOnly, like this:
function handleScroll(e) {
if (e.target.tagName.toLowerCase() === 'input'
&& (e.target.type === 'number')
&& (e.target === document.activeElement)
&& !e.target.readOnly
) {
e.target.readOnly = true;
setTimeout(function(el){ el.readOnly = false; }, 0, e.target);
}
}
document.addEventListener('wheel', function(e){ handleScroll(e); });
One side effect that I've noticed is that it may cause the field to flicker for a split-second if you have different styling for readOnly fields, but for my case at least, this doesn't seem to be an issue.
Similarly, (as explained in James' answer) instead of modifying the readOnly property, you can blur() the field and then focus() it back, but again, depending on styles in use, some flickering might occur.
Alternatively, as mentioned in other comments here, you can just call preventDefault() on the event instead. Assuming that you only handle wheel events on number inputs which are in focus and under the mouse cursor (that's what the three conditions above signify), negative impact on user experience would be close to none.
function fixNumericScrolling() {
$$( "input[type=number]" ).addEvent( "mousewheel", function(e) {
stopAll(e);
} );
}
function stopAll(e) {
if( typeof( e.preventDefault ) != "undefined" ) e.preventDefault();
if( typeof( e.stopImmediatePropagation ) != "undefined" ) e.stopImmediatePropagation();
if( typeof( event ) != "undefined" ) {
if( typeof( event.preventDefault ) != "undefined" ) event.preventDefault();
if( typeof( event.stopImmediatePropagation ) != "undefined" ) event.stopImmediatePropagation();
}
return false;
}
Most answers blur the number element even if the cursor isn't hovering over it; the below does not
document.addEventListener("wheel", function(event) {
if (document.activeElement.type === "number" &&
document.elementFromPoint(event.x, event.y) == document.activeElement) {
document.activeElement.blur();
}
});
https://jsfiddle.net/s06puv3j/1/
I was struggling with the solution. So, This and other posts help me to do this. We need to change some stuff regarding the best answer here. So in order to disable scrolling, we must add the following:
<script>
$(document).ready(function() {
$('input[type=number]').on('wheel',function(e){ $(this).blur(); });
});
</script>
Instead of using "onwheel" we use "wheel" :)
Antd / React + Typescript answer
const myComponent = () => {
const inputRef: React.RefObject<HTMLInputElement> = createRef();
return <Input
ref={inputRef}
type="number"
onWheel={(e) => {
if (inputRef && inputRef.current && inputRef.current.blur) {
inputRef.current.blur();
}
e.preventDefault();
}}
/>
}
Angular solution. One directive to rule them all!
In contrast to other solutions, with this solution the user
does not loose focus on the input
and still able to scroll!
See it on StackBlitz
import { Directive, ElementRef, NgZone, OnDestroy } from '#angular/core';
import { fromEvent, Subscription, takeUntil } from 'rxjs';
import { tap, switchMap } from 'rxjs/operators';
#Directive({
selector: 'input[type=number]',
})
export class FixNumberInputScrollDirective implements OnDestroy {
private subs = new Subscription();
constructor(elRef: ElementRef<HTMLInputElement>, zone: NgZone) {
const el = elRef.nativeElement;
const focus$ = fromEvent(el, 'focus');
const blur$ = fromEvent(el, 'blur');
// when input is focused, start listening to the scroll of element. On this event blur and
// re-focus on the next tick. This allows for the page scroll to still happen, but the unwanted
// input number change is prevented.
// Stop listening to the scroll when focus is lost
const preventWheel$ = focus$.pipe(
switchMap(() => {
return fromEvent(el, 'wheel', { passive: false }).pipe(
tap(() => {
zone.runOutsideAngular(() => {
el.blur();
setTimeout(() => {
el.focus();
}, 0);
})
}),
takeUntil(blur$)
);
})
);
this.subs.add(preventWheel$.subscribe());
}
ngOnDestroy() {
this.subs.unsubscribe();
}
}