user3287019
user3287019

Reputation: 321

iframe with scrollbar = does not fire scroll event (angular2)

I have an angular2 application which has a header and the content is inside an iframe. I disabled the window scrollbar inside the css file with: html {overflow-y: hidden;} and my goal is to achive a sticky header with an animation.

I tried some options, but in my opinion none of them are fireing / listening to the scroll event of the iframe (window or doscument scroll event is fireing without problems, but I do not need it):

option 1 (when I try (load) instead of (scroll), it works):

<iframe src=".." scrolling="yes" (scroll)="iframeScrollHandler(event)">..</iframe>

option 2:

@ViewChild('iframe') iframe: ElementRef;
constructor(@Inject(DOCUMENT) private document: Document, private window: Window, private renderer:Renderer) {
  }

  ngAfterViewInit(){
    this.renderer.listen(this.iframe.nativeElement, 'scroll', (event) => {
       console.log('scroll scroll..');
    })

  }

Do you know why the scroll event is not fireing?

Upvotes: 0

Views: 3078

Answers (1)

user3262600
user3262600

Reputation: 494

You may have found a solution to this already but I came across exactly this issue recently and found this question without an answer. The key seems to be the timing of when you register for event notifcations. You have to register for the scroll event after the iframe's onload event has fired. In my code I registered for the iframe's onload event in my template code.

<form ngNoForm action="/logout" method="post" role="form"> 
    <div class="container-fluid main">
        <iframe #tframe [src]="src" class="frame" (load)="onFrameLoad()"> </iframe>
        <div class="clearfix">
            <div class="pull-right">
                <button type="submit" class="btn btn-dialog-secondary">{{'common.decline' | i18n}}</button>
                <button type="button" [disabled]="!scrollBottom" id="defaultFocus" class="btn btn-primary" (click)="accept()">{{'common.accept' | i18n}}</button>
            </div>
        </div>
    </div>
</form>

In the class itself I had the iframe ElementRef member (aswell as a boolean member that I wanted to update in response to the scroll event, more on that later in this answer..).

export class RouteFrame implements AfterViewInit {
  scrollBottom: boolean = false;
  @ViewChild('tframe') frame : ElementRef;

  scroll$: any = null;

}

Then in the onFrameLoad() method of the RouteFrame class register for the scroll event.

onFrameLoad() {

     this.scroll$ = Observable.fromEvent(this.frame.nativeElement.contentWindow, 'scroll')
        .debounceTime(200)
        .subscribe((evt: any) => {
            this.onScroll();
        });
}

Then in the onScroll() method, do whatever logic you need. In my case I was watching when the user scrolled to the bottom of the iframe. However what I found was that the scroll event was happening 'outside' of Angular, so the form was re-evaluated by Angular on the change in value of that variable, so the accept button never got enabled even though it had the [disabled]="!scrollBottom" as part of it's declaration. Hence why the update to the scrollBottom variable is wrapped in this.zone.run().

onScroll() {

    if (this.frame.nativeElement.contentWindow.scrollY > this.frame.nativeElement.contentWindow.innerHeight) {
        this.zone.run(() => {
            this.scrollBottom = true;
        });
    }
}

this.zone is just injected like other Angular providers in the cstr of the RouteFrame class.

constructor(@Inject(NgZone) private zone: NgZone, ....)

For completeness unsubscribe to the scroll event listener when the component is finished with.

NgOnDestroy() {
    this.scroll$.unsubscribe();
}

Angular version 4.3.0

Upvotes: 2

Related Questions