admin管理员组文章数量:1327667
I have a problem with RxJs.
I need to log a message in console.log when I click once, and different message when I click twice on the button. The problems are:
at the beginning if I click first time - nothing happens - wrong (I should see first message 'one click')
then if I click on the button and then (after one second) I click again, for the second click I can see two messages - wrong (I should see only one message per action)
- when I click on the button, wait a second, click again, wait a second and click again, then for the last click I will see three messages in console.log - wrong (I should see only one message per action)
- after that if I click twice (doubleclick) I will see five messages for double click, and one for one click - wrong (I should see only one message 'double click')
all I want is:
- if I click once, I need to see only one message ('one click')
- if I click twice (doubleclick), still I need to see only one message ('double click')
- if I click more than twice - nothing happens
any help?
check hear for examples
I have a problem with RxJs.
I need to log a message in console.log when I click once, and different message when I click twice on the button. The problems are:
at the beginning if I click first time - nothing happens - wrong (I should see first message 'one click')
then if I click on the button and then (after one second) I click again, for the second click I can see two messages - wrong (I should see only one message per action)
- when I click on the button, wait a second, click again, wait a second and click again, then for the last click I will see three messages in console.log - wrong (I should see only one message per action)
- after that if I click twice (doubleclick) I will see five messages for double click, and one for one click - wrong (I should see only one message 'double click')
all I want is:
- if I click once, I need to see only one message ('one click')
- if I click twice (doubleclick), still I need to see only one message ('double click')
- if I click more than twice - nothing happens
any help?
check hear for examples
Share Improve this question edited Sep 6, 2018 at 3:59 Dulanga Heshan 1,4171 gold badge22 silver badges38 bronze badges asked Sep 5, 2018 at 20:45 PrzemoPrzemo 5381 gold badge8 silver badges22 bronze badges 5- 2 please put your code in your question as well – bryan60 Commented Sep 5, 2018 at 20:47
- yeah I forget, thx – Przemo Commented Sep 5, 2018 at 20:48
- have you tried angulars (dblclick) binding instead of a plex observable? – bryan60 Commented Sep 5, 2018 at 20:50
- of course, but I need another solution for ios where dblclick is not working properly – Przemo Commented Sep 5, 2018 at 20:52
-
1
FYI, the reason you're getting multiples of each message is because you are resubscribing to the observable every time the
test()
function is called. You should unsubscribe after the observable subscription callback fires. Which begs the question, why are you using observables in the first place? Seems like a regular click handler should do, yeah? – mhodges Commented Sep 5, 2018 at 21:24
3 Answers
Reset to default 4A very simple answer (not using any observables) is to use a setTimeout()
and check on each click if the timeout is already set, if so, you know it's a second click within a given time window (double click) and if not, it's the first click. If the timeout expires, you know it was just a single click, like so:
Updated StackBlitz
// have a timer that will persist between function calls
private clickTimeout = null;
public test(event): void {
// if timeout exists, we know it's a second click within the timeout duration
// AKA double click
if (this.clickTimeout) {
// first, clear the timeout to stop it from pleting
clearTimeout(this.clickTimeout);
// set to null to reset
this.clickTimeout = null;
// do whatever you want on double click
console.log("double!");
} else {
// if timeout doesn't exist, we know it's first click
this.clickTimeout = setTimeout(() => {
// if timeout expires, we know second click was not handled within time window
// so we can treat it as a single click
// first, reset the timeout
this.clickTimeout = null;
// do whatever you want on single click
console.log("one click");
}, 400);
}
}
EDIT
I missed the part about ignoring any more than 2 clicks. It's not that much more work, but I broke it out a bit more to be able to reuse code, so it looks like a lot more. Anyway, to ignore 3+ clicks, it would look like the following:
// count the clicks
private clicks = 0;
private clickTimeout = null;
public test(event): void {
this.clicks++;
if (this.clickTimeout) {
// if is double click, set the timeout to handle double click
if (this.clicks <= 2) {
this.setClickTimeout(this.handleDoubleClick);
} else {
// otherwise, we are at 3+ clicks, use an empty callback to essentially do a "no op" when pleted
this.setClickTimeout(() => {});
}
} else {
// if timeout doesn't exist, we know it's first click - treat as single click until further notice
this.setClickTimeout(this.handleSingleClick);
}
}
// sets the click timeout and takes a callback for what operations you want to plete when the
// click timeout pletes
public setClickTimeout(cb) {
// clear any existing timeout
clearTimeout(this.clickTimeout);
this.clickTimeout = setTimeout(() => {
this.clickTimeout = null;
this.clicks = 0;
cb();
}, 400);
}
public handleSingleClick() {
console.log("one click");
}
public handleDoubleClick() {
console.log("double!");
}
@Siddharth Ajmeras answer shows how to do this with events. I was not aware a dblclick event existed. The more you know. If you are still interested on how to do this with rxjs, here is an example.
// How fast does the user has to click
// so that it counts as double click
const doubleClickDuration = 100;
// Create a stream out of the mouse click event.
const leftClick$ = fromEvent(window, 'click')
// We are only interested in left clicks, so we filter the result down
.pipe(filter((event: any) => event.button === 0));
// We have two things to consider in order to detect single or
// or double clicks.
// 1. We debounce the event. The event will only be forwared
// once enough time has passed to be sure we only have a single click
const debounce$ = leftClick$
.pipe(debounceTime(doubleClickDuration));
// 2. We also want to abort once two clicks have e in.
const clickLimit$ = leftClick$
.pipe(
bufferCount(2),
);
// Now we bine those two. The gate will emit once we have
// either waited enough to be sure its a single click or
// two clicks have passed throug
const bufferGate$ = race(debounce$, clickLimit$)
.pipe(
// We are only interested in the first event. After that
// we want to restart.
first(),
repeat(),
);
// Now we can buffer the original click stream until our
// buffer gate triggers.
leftClick$
.pipe(
buffer(bufferGate$),
// Here we map the buffered events into the length of the buffer
// If the user clicked once, the buffer is 1. If he clicked twice it is 2
map(clicks => clicks.length),
).subscribe(clicks => console.log('CLicks', clicks));
This is either what you want or very close to it:
import { Component, ViewChild, ElementRef } from '@angular/core';
import { fromEvent } from 'rxjs';
@Component({
selector: 'my-app',
templateUrl: './app.ponent.html',
styleUrls: [ './app.ponent.css' ]
})
export class AppComponent {
@ViewChild('mybutton') button: ElementRef;
ngAfterViewInit() {
fromEvent(this.button.nativeElement, 'dblclick')
.subscribe(e => console.log('double click'));
fromEvent(this.button.nativeElement, 'click')
.subscribe((e: MouseEvent) => {
if (e.detail === 1) console.log('one click') // could use a filter inside a pipe instead of using an if statement
// if (e.detail === 2) console.log('double click') // an alternative for handling double clicks
});
}
}
and the HTML:
<button #mybutton>Test</button>
This solution uses event.detail and is loosely based on this useful post - Prevent click event from firing when dblclick event fires - which not only discusses event.detail but also looks at the timing issues involved.
One of the big probs with your code is that you are subscribing to the event inside a function that is called multiple times, which means each time the button is clicked you create another subscription. Using ngAfterViewInit
(which is only called once as part of the lifecycle) prevents this issue (and ensures the DOM is loaded).
Here's a stackblitz for you:
https://stackblitz./edit/angular-t6cvjj
Forgive me for my sloppy type declarations!
This satisfies your requirements as follows:
- If I click once, I need to see only one message ('one click') - PASS
- If I click twice (doubleclick), still I need to see only one message ('double click') - FAIL, you see 'one click' on the 1st click and 'double click' on the 2nd click
- If I click more than twice - nothing happens - PASS
Due to the timing issues discussed in the SO post given, how you solve this is a matter of preference and thus I decided not to tackle it. And plus, you are not paying me ;) This answer however should set you well on your way to solving it fully.
PS There is a ment on the SO post above that suggests event.detail may not work on IE11
本文标签: javascriptSeparate action for single click and double click with RxJsStack Overflow
版权声明:本文标题:javascript - Separate action for single click and double click with RxJs - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742225891a2436308.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论