2

I am trying to differentiate between mouse and keyboard inputs in an input of type date:

template

<input type="date" (change)="onChange($event)">

controller

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {

  onChange(event) {
    // How to check if user used the mouse to select a date
    // or if he typed a date
    console.log(event);
  }
}

simplest stackblitz: https://stackblitz.com/edit/angular-zz2nbl

Is it possible? Do I need to subscribe to multiple, diffrent events?

Riscie
  • 3,775
  • 1
  • 24
  • 31
  • the problem is, that the (click) event is not behaving the way you would expect for the date input. An event gets fired when the user clicks on the down-arrow for example, but not when he selects a date. @Vega – Riscie Jan 30 '20 at 10:39
  • The type is always `change` if I subscribe to `(change)` @Vega – Riscie Jan 30 '20 at 11:01
  • If you combine it with the presence of (input) and (click), you can distinguish from where the event came ? – Vega Jan 30 '20 at 11:06
  • 1
    Let me try in the demo :) – Vega Jan 30 '20 at 11:06
  • Something like this, although I feel you are looking for something a bit more complex : https://stackblitz.com/edit/angular-ovijfe – Vega Jan 31 '20 at 07:05
  • Thank you @Vega. I like simple over complex ;) The problem still is, that when we use the `(click)` event, we do not get an event when the user actually clicks on a date :( (the event gets triggered when the user clicks on the down-arrow to open the date-view) – Riscie Jan 31 '20 at 07:54

2 Answers2

5

Firs of all here are the definitions of click and change events as per MDN

Click Event

An element receives a click event when a pointing device button (such as a mouse's primary mouse button) is both pressed and released while the pointer is located inside the element.

Change Event

The change event is fired for <input>, <select>, and <textarea> elements when an alteration to the element's value is committed by the user. Unlike the input event, the change event is not necessarily fired for each alteration to an element's value.

Now if you see the click event is available to all elements but change is available to only input , select and textarea. Also is clear from the definition the latter is fired when the value of the element is changed/altered.

So in order to capture both the events Yes you will have to capture both separately like below.

<input type="date" #input [ngModel]="myInput" (change)="onChange($event)" (click)="onClick($event)" (keydown)="onKeydown($event)" >


export class AppComponent {
  isKeyboardChange: boolean;
  isDatepickerChange: boolean;

  onChange(event) {
    // How to check if user used the mouse to select a date
    // or if he typed a date
    console.log(event.type);
    if (this.isKeyboardChange) {
      console.log("Was changes using Keyboard");
    }

    if (this.isDatepickerChange) {
      console.log("Was changes using date picker");
    }
  }

  onClick(event) {
    this.isKeyboardChange = false;
    this.isDatepickerChange = true;
  }

  onKeydown(event) {
    this.isDatepickerChange = false;
    this.isKeyboardChange = true;
  }
}

Also with the above code you will see. When the user click on the input be it the date or the arrow, the logged value on the console will bee click and when he selects a different value from the current one the value logged will be change.

Also if you use tab to move between input elements click event wont trigger in that case focus will be fired.

Javascript Events: this is a list of events available to all the HTML Elements. This does not include the change event as its only applicable to input, select and textarea.

So the short answer to your question will be that every type of event you want to capture you will have to have a corresponding eventListener/eventHandler.

UPDATE: And in this case you might want to use keydown event which fires whenever a keyboard key is pressed down. So in this case when the user changes the value using a keyboard a keydown event will be fired just before change event and whenever a key is pressed down while the focus is on the input else if user uses the datepicker click will be fired just before change. Also when the user selects the date no click will be fired it will only fire when user clicks on the textbox to open the picker. So using these you will be able to figure out how the value was changed.

UPDATE2: edited to use keydown event instead of keypress as keypress does not captures the arrow keys and the input type date can be changed using arrow keys as well.

UPDATE3:

Browser Compatibility:

  1. Chrome: OK
  2. Firefox: OK
  3. Safari: Does not support date picker
  4. IE: Does not support date picker
  5. Edge: Supports only date picker. But arrow keys are allowed to change the date when datepicker is open (this specific case need to be handled separately as user is actually using datepicker but arrow keys may give a false impression)

Below is a small demo for the same (not in angular) but will behave exactly how it will in angular. The principles are exactly same. Have updated the stackblitz demo and it logs the way user used to change the value i.e. keyboard or datepicker.

Stackblitz Demo

function onChange(event) {
  console.log(event.type)
}

function onClick(event) {
  console.log(event.type)
}

function onFocus(event) {
  console.log(event.type)
}


function onKeypress(event) {
  console.log(event.type)
}
<input type="date" onclick="onClick(event)" onchange="onChange(event)" onfocus="onFocus(event)" onkeypress="onKeypress(event)" onkeydown="onKeypress(event)">

Hope this helps :)

Manish
  • 4,692
  • 3
  • 29
  • 41
  • Hi @Manish. Thank you for the detailed answer. I am aware of the diffrent event types. The problem I am trying to solve it to know, wether the change to the date value was made by a click or by keyboard input. I don't think your example can demonstrate that? – Riscie Feb 07 '20 at 09:39
  • Hi again! Could you link to the updated stackblitz? I think the one I've created is still unchanged? – Riscie Feb 07 '20 at 09:56
  • Hey @Manish. I don't think your updated example solves my problem. In the resulting change event I still can't decide wether the user used the mouse or keyboard to initiate the change. Have a look at `sportzpikachu`s answer. He has it working the way I need it. But there I am not sure if the event order is always the same. – Riscie Feb 07 '20 at 10:20
  • @Riscie thats correct the order wont be same as per the other answer. But with the update i have made the order will be always the same i.e keypress first and then change and click first and then change. Let me do one thing update the stackblitz accordingly – Manish Feb 07 '20 at 10:34
  • @Riscie update the answer and a working demo on stackblitz. Hope this solves your problem :) – Manish Feb 07 '20 at 10:53
  • Thanks again for your effort! This is now the same answer as sportzpikachus gave ;) I am still not sure wether the event order is the same in all browsers, right? – Riscie Feb 07 '20 at 10:56
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/207412/discussion-between-manish-and-riscie). – Manish Feb 07 '20 at 10:56
  • Thanks for clarifying and testing in diffrent browsers. Will accept this answer after the bounty-block-time. – Riscie Feb 07 '20 at 12:47
1

Try using onMouseDown and onKeyDown. mouseLast would be a boolean whether or not the last event was mouseDown.

template

<input type="date" (change)="onChange($event)" (keydown)="onKey()" (mousedown)="onMouse()">

controller

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  onMouse() {
    this.mouseLast = true;
  }
  onKey() {
    this.mouseLast = false;
  }
  onChange(event) {
    if (this.mouseLast == undefined || this.mouseLast == null) {
        return;
    }
    console.log(this.mouseLast);
  }
}
sportzpikachu
  • 831
  • 5
  • 18
  • Thank you for the answer. I was thinking about this solution. Can we actually be sure, that the `mousedown` and `keydown` event always fire before the `change` event? If this is the case and there is some documentation for this, I would look at this as a valid answer. – Riscie Feb 07 '20 at 09:43
  • I did some research. It seems like the event order is not defined by the js standard. Browsers are free to implement them they want. However I see that it would always make sense to fire `mousedown` and `keydown` before `change`, so I am not sure wether the answer is correct in all browsers... (reference: https://stackoverflow.com/questions/282245/dom-event-precedence) – Riscie Feb 07 '20 at 10:23
  • Hey, @Riscie, sorry I couldn't get back to you earlier, but as you said, the `mousedown` and `keydown` events may not fire before the `change` event. So you could perhaps add a `if` statement to check whether or not `mouseLast` is declared? Hopefully that helps you! I've edited my answer to reflect these changes. – sportzpikachu Feb 08 '20 at 03:01