Differentiate between focus event triggered by keyboard/mouse - javascript

I'm using jquery ui autocomplete and want to decipher between focus events triggered by keyboard interaction and mouse interaction. How would I go about this?
$('input').autocomplete({
source: function(request, response) {
...
},
focus: function(event, ui) {
// If focus triggered by keyboard interaction
alert('do something');
// If focus event triggered by mouse interaction
alert('do something else');
}
});
Thanks

The only way I can think of doing this is to have a handler listen in on the keypress and click events, and toggle a boolean flag on/off. Then on the focus handler of your input, you can just check what the value of your flag is, and go from there.
Probably something like
var isClick;
$(document).bind('click', function() { isClick = true; })
.bind('keypress', function() { isClick = false; })
;
var focusHandler = function () {
if (isClick) {
// clicky!
} else {
// tabby!
}
}
$('input').focus(function() {
// we set a small timeout to let the click / keypress event to trigger
// and update our boolean
setTimeout(focusHandler,100);
});
Whipped up a small working prototype on jsFiddle (don't you just love this site?). Check it out if you want.
Of course, this is all running off a focus event on an <input>, but the focus handler on the autocomplete works in the same way.
The setTimeout will introduce a bit of lag, but at 100ms, it might be negligible, based on your needs.

You should actually be able to determine this from the event-Object that is passed into the focus-event. Depending on your code structure this might be different, but there is usually a property called originalEvent in there, which might be nested to some depth. Examine the event-object more closely to determine the correct syntax. Then test on mousenter or keydown via regular expression. Something like this:
focus: function(event, ui){
if(/^key/.test(event.originalEvent.originalEvent.type)){
//code for keydown
}else{
//code for mouseenter and any other event
}
}

The easiest and most elegant way I've found of achieving this is to use the "What Input?" library. It's tiny (~2K minified), and gives you access to the event type both in scripts:
if (whatInput.ask() === 'mouse') {
// do something
}
...and also (via a single data attribute that it adds to the document body) styles:
[data-whatinput="mouse"] :focus,
[data-whatinput="touch"] :focus {
// focus styles for mouse and touch only
}
I particularly like the fact that where you just want a different visual behaviour for mouse / keyboard it makes it possible to do that in the stylesheet (where it really belongs) rather than via some hacky bit of event-checking Javascript (though of course if you do need to do something that's not just purely visual, the former approach lets you handle it in Javascript instead).

The first thing that comes to mind is that you can find the position of the mouse and check to see if its within the position of the element
Use this to store the position of the element:
var input = $('#your_autocompleted_element_id'),
offset = input.offset(),
input_x = offset.top,
input_y = offset.left,
input_w = input.outerWidth(),
input_h = input.outerHeight();
Then use this to find absolute position of the mouse within the window:
var cur_mx, cur_my;
$(document).mousemove(function(e){
cur_mx = e.pageX;
cur_my = e.pageY;
});
Then in your autcomplete setup:
focus: function(event, ui) {
// mouse is doing the focus when...
// mouse x is greater than input x and less than input x + input width
// and y is greater than input y and less than input y + input height
if (cur_mx >= input_x && cur_mx <= input_x + input_w && cur_my >= input_y && cur_my <= input_y + input_h) {
// do your silly mouse focus witchcraft here
} else {
// keyboard time!
}
}

This can be handled using mousedown event, see my example below.
this.focusFrom = 'keyboard' =>
onFocus = () => {
if (this.focusFrom === 'keyboard') {
// do something when focus from keyboard
}
}
handleMouseDown = () => {
this.focusFrom = 'mouse';
}
handleOnClick = () => {
this.focusFrom = 'keyboard';
}

Related

HTML Spinbutton up/down arrows event [duplicate]

I want to be able to listen to <input type="number" /> step UP (increment) and step down events with jQuery. (currently I can only understand how to listen to change event)
For input type="number", you can use the change event, but will possibly have more cases to handle and create some clutter. I broke it down, I recommend using the "mouseup" event for the increment feature (which will mainly be used from pc) But if the user uses a device instead, I would use the event 'keyup' since the increment feature will not appear and the user will have an on-screen keyboard instead. The change event will listen for both.
For example :
$(".counter").bind('mouseup', function () {
if($(this).val() == undefined || $(this).val() == "")
return; /* Exit dont bother with handling this later, if its not needed leave. */
/* Important Check against integers and not strings. */
/* Comparing against arrays will give unexecpted behavior, size is different then the value. */
var newVal = parseInt($(this).val());
var oldVal = parseInt($(this).data('old-value'));
if (oldVal < newVal) {
alert('incrementing');
}
else{
alert('decrementing');
}
$(this).data('old-value', $(this).val());
});
$(".counter").bind('keyup', function () {
/* Similar logic */
});
I use "bind" instead of "on" or the by method "change" since "on" is a shorthand for bind.
JSFiddle Demo
There is no event for up and down. You can use change event
$(".counter").change(function () {
alert($(this).val());
})
DEMO
You can try something like, You can store previous value and compare with currently value and identify up or down
$(".counter").change(function () {
if ($(this).data('old-value') < $(this).val()) {
alert('Alert up');
} else {
alert('Alert dowm');
}
$(this).data('old-value', $(this).val());
})
DEMO
I'm not sure if there is a listener of the stepUp and StepDown but you can externalize the PLUS and LESS buttons of the input number using:
document.getElementById("myNumber").stepUp(5); //increase the value +5
document.getElementById("myNumber").stepDown(5); // decrease the value -5
So you can finally achieve your objective!
Reference: https://www.w3schools.com/jsref/met_number_stepup.asp
NOTE: Be aware that IE11 and EDGE doesn't implement stepUp / stepDown. On my case I just remove the + and - icon and leave only the input number field. Hope EDGE die soon...
I know this is a relatively old question but, came across it in my search.
In case anyone comes here looking for the answer to this on a custom element triggering the stepUp() or stepDown() like me and not wanting to use jQuery, the below should help:
On the element handling the click to trigger the stepUp()/stepDown(), add this (and change the elements to whatever relation they have to each other):
onclick="this.elementToStepDown.stepDown();this.elementToStepDown.dispatchEvent(new Event('change'))"
Fuller example:
<input class="input--hide_controls" id="number_of_ducks" type="number" value="7" step="1"/>
<div class="increment" onclick="this.parentNode.querySelector('input').stepUp();this.parentNode.querySelector('input').dispatchEvent(new Event('change'))"><i class="im im-plus"></i></div>
Or, extract it out and put it in a JS function that does the same and call that onclick:
let increment = (element) => {
element.stepUp();
element.dispatchEvent(new Event('change'))
}
let decrement = (element) => {
element.stepDown();
element.dispatchEvent(new Event('change'))
}
Then listen out for the 'change' event on the input.
There's no specific event for up and down. Your closest bet is to use the change event. You might also take a look at the oninput event but I am not sure it is supported by all browsers:
$('#myinput').on('input', function() {
});
You should do this:
var $counter = $("your_counter");
var number = $counter.val();
$counter.change(function () {
if($counter.val() > number)
alert('up');
else
alert('down');
number = $counter.val();
});
Here is the demo: http://jsfiddle.net/6bb8S/
html
<input type="number" data-number="0" class="counter" value="0" />
you need to put the same value in the data-number and in the value
jquery
$(document).on('change', '.counter', function() {
//get number of input
var number = $(this).attr("data-number");
//console.log(number);
if($(this).val() > number){
alert('up');
}else{
alert('down');
}
//set number of input for the next step
$(this).attr("data-number", $(this).val());
});
https://jsfiddle.net/maicon_card/46hdm8pj/
works with multiple inputs

Execute javascript only above certain width with resize

I'm currently developing a website which has a sticky menu function. I've got the normal javascript to work good, which adds some classes once the client scrolls past 150px.
I now face the problem that I don't want the classes to be added once people view the website below 725px, so I added a rule that it only executes the script above 725px but the problem is this:
If I resize the window back to full the function won't work anymore, so I created another rule with the javascript resize function but I can't get it to work..
Here is my script:
$(document).ready(function(){
var mainbottom = 150;
if($(window).innerWidth() > 725) {
$(window).on('scroll',function(){
stop = Math.round($(window).scrollTop());
if (stop > mainbottom) {
$('.header').addClass('sticky-nav');
$('.logo').addClass('sticky-logo');
$('.navigation').addClass('sticky-menu');
} else {
$('.header').removeClass('sticky-nav');
$('.logo').removeClass('sticky-logo');
$('.navigation').removeClass('sticky-menu');
}
});
}
});
$(window).resize(function() {
var mainbottom = 150;
if($(window).innerWidth() > 725) {
$(window).on('scroll',function(){
stop = Math.round($(window).scrollTop());
if (stop > mainbottom) {
$('.header').addClass('sticky-nav');
$('.logo').addClass('sticky-logo');
$('.navigation').addClass('sticky-menu');
} else {
$('.header').removeClass('sticky-nav');
$('.logo').removeClass('sticky-logo');
$('.navigation').removeClass('sticky-menu');
}
});
}
});
I'll hope somebody can help me with this problem.
First off, you should keep your code DRY. So preferably never copy paste any code around, bacause you will have to edit all the copies when you have to alter the behaviour or fix bugs.
You have not but your second $(window).resize() handler in the onready handler, so maybe that is why it is not triggered.
This should work:
$(document).ready(function(){
var mainbottom = 150;
function onScroll () {
stop = Math.round($(window).scrollTop());
if (stop > mainbottom) {
$('.header').addClass('sticky-nav');
$('.logo').addClass('sticky-logo');
$('.navigation').addClass('sticky-menu');
} else {
$('.header').removeClass('sticky-nav');
$('.logo').removeClass('sticky-logo');
$('.navigation').removeClass('sticky-menu');
}
}
var widthExceeded = false;
$(window).resize(function() {
$(window).innerWidth() > 725) {
if (!widthExceeded) {
$(window).on('scroll', onScroll);
}
widthExceeded = true;
} else {
if (widthExceeded) {
$(window).off('scroll', onScroll);
}
widthExceeded = false;
}
}).resize();
});
You are defining a scroll event listener inside a resize event listener, so basically you're declaring the scroll listener on every resive event (so the scroll listener is defined many many times if the user resize its browser). You need to correct this.
You could declare a flag (boolean) to indicate wether the viewport is below 725px or not. It should be initialized on $(document).ready(...) by testing the viewport dimensions.
Create a resize event listener which updates this flag by testing the viewport width, so you always know if you need to manage your classes or not.
At this point, console.log(your_flag) in your resize event listener to check if it works fine.
Then declare a scroll event listener, and in this listener the first thing you want to do is test the flag value. If viewport > 725, then manage the classes, otherwise do nothing.

Change-Event for div [duplicate]

I want to run a function when a user edits the content of a div with contenteditable attribute. What's the equivalent of an onchange event?
I'm using jQuery so any solutions that uses jQuery is preferred. Thanks!
2022 update
As pointed out in the comments, this doesn't answer the question asked, which wanted the equivalent of the change event rather than the input event. However, I'll leave it here as is.
Original answer
I'd suggest attaching listeners to key events fired by the editable element, though you need to be aware that keydown and keypress events are fired before the content itself is changed. This won't cover every possible means of changing the content: the user can also use cut, copy and paste from the Edit or context browser menus, so you may want to handle the cut copy and paste events too. Also, the user can drop text or other content, so there are more events there (mouseup, for example). You may want to poll the element's contents as a fallback.
UPDATE 29 October 2014
The HTML5 input event is the answer in the long term. At the time of writing, it is supported for contenteditable elements in current Mozilla (from Firefox 14) and WebKit/Blink browsers, but not IE.
Demo:
document.getElementById("editor").addEventListener("input", function() {
console.log("input event fired");
}, false);
<div contenteditable="true" id="editor">Please type something in here</div>
Demo: http://jsfiddle.net/ch6yn/2691/
Here is a more efficient version which uses on for all contenteditables. It's based off the top answers here.
$('body').on('focus', '[contenteditable]', function() {
const $this = $(this);
$this.data('before', $this.html());
}).on('blur keyup paste input', '[contenteditable]', function() {
const $this = $(this);
if ($this.data('before') !== $this.html()) {
$this.data('before', $this.html());
$this.trigger('change');
}
});
The project is here: https://github.com/balupton/html5edit
Consider using MutationObserver. These observers are designed to react to changes in the DOM, and as a performant replacement to Mutation Events.
Pros:
Fires when any change occurs, which is difficult to achieve by listening to key events as suggested by other answers. For example, all of these work well: drag & drop, italicizing, copy/cut/paste through context menu.
Designed with performance in mind.
Simple, straightforward code. It's a lot easier to understand and debug code that listens to one event rather than code that listens to 10 events.
Google has an excellent mutation summary library which makes using MutationObservers very easy.
Cons:
Requires a very recent version of Firefox (14.0+), Chrome (18+), or IE (11+).
New API to understand
Not a lot of information available yet on best practices or case studies
Learn more:
I wrote a little snippet to compare using MutationObserers to handling a variety of events. I used balupton's code since his answer has the most upvotes.
Mozilla has an excellent page on the API
Take a look at the MutationSummary library
non jQuery quick and dirty answer:
function setChangeListener (div, listener) {
div.addEventListener("blur", listener);
div.addEventListener("keyup", listener);
div.addEventListener("paste", listener);
div.addEventListener("copy", listener);
div.addEventListener("cut", listener);
div.addEventListener("delete", listener);
div.addEventListener("mouseup", listener);
}
var div = document.querySelector("someDiv");
setChangeListener(div, function(event){
console.log(event);
});
I have modified lawwantsin 's answer like so and this works for me. I use the keyup event instead of keypress which works great.
$('#editor').on('focus', function() {
before = $(this).html();
}).on('blur keyup paste', function() {
if (before != $(this).html()) { $(this).trigger('change'); }
});
$('#editor').on('change', function() {alert('changed')});
Two options:
1) For modern (evergreen) browsers:
The "input" event would act as an alternative "change" event.
https://developer.mozilla.org/en-US/docs/Web/Events/input
document.querySelector('div').addEventListener('input', (e) => {
// Do something with the "change"-like event
});
or
<div oninput="someFunc(event)"></div>
or (with jQuery)
$('div').on('click', function(e) {
// Do something with the "change"-like event
});
2) To account for IE11 and modern (evergreen) browsers:
This watches for element changes and their contents inside the div.
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
var div = document.querySelector('div');
var divMO = new window.MutationObserver(function(e) {
// Do something on change
});
divMO.observe(div, { childList: true, subtree: true, characterData: true });
const p = document.querySelector('p')
const result = document.querySelector('div')
const observer = new MutationObserver((mutationRecords) => {
result.textContent = mutationRecords[0].target.data
// result.textContent = p.textContent
})
observer.observe(p, {
characterData: true,
subtree: true,
})
<p contenteditable>abc</p>
<div />
Here's what worked for me:
var clicked = {}
$("[contenteditable='true']").each(function(){
var id = $(this).attr("id");
$(this).bind('focus', function() {
// store the original value of element first time it gets focus
if(!(id in clicked)){
clicked[id] = $(this).html()
}
});
});
// then once the user clicks on save
$("#save").click(function(){
for(var id in clicked){
var original = clicked[id];
var current = $("#"+id).html();
// check if value changed
if(original != current) save(id,current);
}
});
This thread was very helpful while I was investigating the subject.
I've modified some of the code available here into a jQuery plugin so it is in a re-usable form, primarily to satisfy my needs but others may appreciate a simpler interface to jumpstart using contenteditable tags.
https://gist.github.com/3410122
Update:
Due to its increasing popularity the plugin has been adopted by Makesites.org
Development will continue from here:
https://github.com/makesites/jquery-contenteditable
Non JQuery answer...
function makeEditable(elem){
elem.setAttribute('contenteditable', 'true');
elem.addEventListener('blur', function (evt) {
elem.removeAttribute('contenteditable');
elem.removeEventListener('blur', evt.target);
});
elem.focus();
}
To use it, call on (say) a header element with id="myHeader"
makeEditable(document.getElementById('myHeader'))
That element will now be editable by the user until it loses focus.
In Angular 2+
<div contentEditable (input)="type($event)">
Value
</div>
#Component({
...
})
export class ContentEditableComponent {
...
type(event) {
console.log(event.data) // <-- The pressed key
console.log(event.path[0].innerHTML) // <-- The content of the div
}
}
To avoid timers and "save" buttons, you may use blur event wich fires when the element loses focus. but to be sure that the element was actually changed (not just focused and defocused), its content should be compared against its last version. or use keydown event to set some "dirty" flag on this element.
Here is the solution I ended up using and works fabulously. I use $(this).text() instead because I am just using a one line div that is content editable. But you may also use .html() this way you dont have to worry about the scope of a global/non-global variable and the before is actually attached to the editor div.
$('body').delegate('#editor', 'focus', function(){
$(this).data('before', $(this).html());
});
$('#client_tasks').delegate('.task_text', 'blur', function(){
if($(this).data('before') != $(this).html()){
/* do your stuff here - like ajax save */
alert('I promise, I have changed!');
}
});
You need to use input event type
Demo
HTML
<div id="editor" contenteditable="true" >Some text here</div>
JS
const input = document.getElementById('editor');
input.addEventListener('input', updateValue);
function updateValue(e) {
console.log(e.target);
}
know more
The onchange event doesn't fires when an element with the contentEditable attribute is changed, a suggested approach could be to add a button, to "save" the edition.
Check this plugin which handles the issue in that way:
Creating a quick and dirty jQuery contentEditable Plugin
Using DOMCharacterDataModified under MutationEvents will lead to the same. The timeout is setup to prevent sending incorrect values (e.g. in Chrome I had some issues with space key)
var timeoutID;
$('[contenteditable]').bind('DOMCharacterDataModified', function() {
clearTimeout(timeoutID);
$that = $(this);
timeoutID = setTimeout(function() {
$that.trigger('change')
}, 50)
});
$('[contentEditable]').bind('change', function() {
console.log($(this).text());
})
JSFIDDLE example
I built a jQuery plugin to do this.
(function ($) {
$.fn.wysiwygEvt = function () {
return this.each(function () {
var $this = $(this);
var htmlold = $this.html();
$this.bind('blur keyup paste copy cut mouseup', function () {
var htmlnew = $this.html();
if (htmlold !== htmlnew) {
$this.trigger('change')
}
})
})
}
})(jQuery);
You can simply call $('.wysiwyg').wysiwygEvt();
You can also remove / add events if you wish
A simple answer in JQuery, I just created this code and thought it will be helpful for others too
var cont;
$("div [contenteditable=true]").focus(function() {
cont=$(this).html();
});
$("div [contenteditable=true]").blur(function() {
if ($(this).html()!=cont) {
//Here you can write the code to run when the content change
}
});
For me, I want to check the input is valid or not.
If valid, then update, Otherwise show an error message and keep the value as same as before.
Skill: When you edit done, usually, it will trigger the blur event.
Example
<span contenteditable="true">try input somethings.</span>
<script>
const elem = document.querySelector(`span`)
let oldValue = elem.innerText
elem.onkeydown = (keyboardEvent) => {
if (keyboardEvent.key === "Enter") {
elem.blur() // set focusout
}
}
elem.onblur = (e) => {
const curValue = elem.innerText
if (curValue === oldValue) {
return
}
if (curValue.length <= 50) { // 👈 Input your conditions.
// 👇 fail
elem.innerText = oldValue
// (Optional) Add error message
elem.insertAdjacentHTML("beforeend", `<span style="margin-left:5px;color:red">error length=${curValue.length}. Must greater than 50. undo to the previous value.</span>`)
const errMsg = elem.querySelector(`span`)
setTimeout(() => errMsg.remove(), 3500) // wait 3.5 second, and then remove it.
return
}
// 👇 OK, update
oldValue = curValue
}
</script>
Check this idea out.
http://pastie.org/1096892
I think it's close. HTML 5 really needs to add the change event to the spec. The only problem is that the callback function evaluates if (before == $(this).html()) before the content is actually updated in $(this).html(). setTimeout don't work, and it's sad. Let me know what you think.
Based on #balupton's answer:
$(document).on('focus', '[contenteditable]', e => {
const self = $(e.target)
self.data('before', self.html())
})
$(document).on('blur', '[contenteditable]', e => {
const self = $(e.target)
if (self.data('before') !== self.html()) {
self.trigger('change')
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Disable scrolling on `<input type=number>`

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();
}
}

Is it possible to trigger Mouseevents by a divcontainer?

I have an div Element with the ID mypointer, wich has an absolute position. I animate this div on a page with jquery. The goal is a presentation where the elements show the same reaktion on the div element like the mousepointer. So I want to simulate mouseover, click and rightclick events.
Is that possible? Can someone give me an example which show me how to do that?
Thank you for your answers
Lara
P.S.
Example here
link text
the red square is over an h1 element. Is it possible to execute the h1 mouseover event, when there is a collision of the mypointer and an h1 element?
I'm not quite sure if I get you well, but to 'simulate' events like mouseover et cetera, you can always use jQuery's .trigger() in a form like:
$('#my_div_id').trigger('mouseover');
You can also call a more 'detailed' version, where you can specify the events arguments
$('#my_div_id').trigger({
type: 'keypress',
which: 13,
ctrlKey: true
});
which infact would simulate a return key while ctrl key is pressed to 'my_div_id'. If you just need the event handler code to execute, use .triggerHandler().
Maybe i don't understand your idea completely, but i wrote some code.
It works very simple. We bind two events "click mouseover" on #mypointer and also on h1 (or any other selector). When the event fires on #mypointer we check every h1 element to match it's position with position of #mypointer and if match -- trigger the event on matched element.
"use strict";
/*global $*/
function getElementCoordinates(el) {
return {
left: el.offsetLeft,
right: el.offsetLeft + el.offsetWidth,
top: el.offsetTop,
bottom: el.offsetTop + el.offsetHeight
};
}
function checkIntersection($el) {
var pointer = getElementCoordinates($('#mypointer')[0]);
var element = getElementCoordinates($el[0]);
if ((pointer.left >= element.left && pointer.left = element.left && pointer.right = element.bottom && pointer.bottom = element.bottom && pointer.top
$(function () {
$('#mypointer').live('click mouseover', function (e) {
//here write selectors you want to check for collision
$('h1').each(function () {
if (checkIntersection($(this))) {
$(this).trigger(e.type);
return false;
}
});
});
$('h1').live('click mouseover', function (e) {
$("#output").html(e.type + ' fired on ' + e.target.nodeName);
});
});
Sorry, parser "eat" checkIntersection function, so full code available on http://www.everfall.com/paste/id.php?263utdc1nmqy
wbr,
Roman.

Categories

Resources