Home > Blockchain >  Rxjs - show/hide element with delay and timer
Rxjs - show/hide element with delay and timer

Time:02-02

I am trying to implement show/hide functionality using Rxjs in Angular.

I have multiple elements on which I can hover (arrows). After hovering over an arrow I am displaying div.

I need hover with some delay but this can be done with debounceTime and this is not a problem

The problem I have is a global timer which I need.

  1. When the user hovers over the arrow I should show div element after 500ms for example
  2. When the user moves the mouse outside the arrow, the timer should start and count 5s and after 5s I should hide the div element
  3. If the user moves the mouse on div which was shown during that 5s, the timer should stop and reset
  4. When the user moves the mouse outside div timer should start again.

I tried:

countdownSeconds = 5;
interval$ = interval(1000).pipe(mapTo(-1));
arrowHover$: Observable<any>;
arrowUnhover$: Observable<any>;

onMouseEnter() {
  this.arrowHover$ = from(EMPTY).pipe(mapTo(false));
}

onMouseLeave() {
  this.arrowUnhover$ = from(EMPTY).pipe(mapTo(true));
  this.setTimer();
}

setTimer() {
  this.timer$ = merge(this.arrowHover$, this.arrowUnhover$)
    .pipe(
      startWith(this.interval$),
      switchMap(val => (val ? this.interval$ : empty())),
      scan((acc, curr) => (curr ? curr   acc : acc), this.countdownSeconds),
      takeWhile(v => v >= 0)
    )
    .subscribe(val => console.log(val));
}

And when I move the mouse outside arrow timer is starting but how to stop it and reset its value? I have no idea what to do next or how to change it to work as expected.

CodePudding user response:

If I understand correctly this is what you are looking for:

countDownMs = 5000; // in milliseconds
arrowHover$ = new Subject();
isDivShowing = false;
isOnDiv = false;

ngOnInit(){
  arrowHover$.pipe(
    debounceTime(this.countDownMs),
    filter(() => !this.isDivShowing || !this.isOnDiv),
  ).subscribe((hover) => {
    console.log(`${hover ? 'show div' : 'hide div'}`);
    this.isDivShowing = hover;
  });
}

onEnterDiv(){
  this.isOnDiv = true;
};

onLeaveDiv(){
  this.isOnDiv = false;
  this.arrowHover$.next(false);
};


onMouseEnter(){
  this.arrowHover$.next(true);
};

onMouseLeft(){
  this.arrowHover$.next(false);
};

Example on stackblitz

CodePudding user response:

Whenever there are things set in motion but possibly reset or overridden by future events, switchMap and takeUntil are operators to keep in mind.

Below is untested because I am responding via mobile phone, but here's the general idea:

const myDiv = document.getElementById('myDiv');

const enter$ = fromEvent(myDiv, 'onMouseEnter').pipe(
  mapTo(true)
);
const exit$ = fromEvent(myDiv, 'onMouseExit').pipe(
  mapTo(false)
);
const destroy$ = NEVER; // Replace this with something that emits once when it should all be recycled, then completes. ...or get rid of this.

let isDisplayed = false;

merge(enter$, exit$).pipe(
  switchMap(isEntering => isEntering === isDisplayed ? EMPTY // Don't emit anything, but cancel whatever might have been brewing from the previous switch map evaluation
    : of(isEntering).pipe(
      delay(isEntering ? 500 : 5000)
    )
  ),
  takeUntil(destroy$)
).subscribe({
  next: isEntering => {
    isDisplayed = isEntering;
    myDiv.style.display = isEntering ? 'block' : 'none';
  }
});
  •  Tags:  
  • Related