2

I have a landing page with a single Foundation 6 Reveal modal. The modal contains the Contact Form for the page. That modal, therefore, can be triggered by several buttons that appear in different locations on the page. All the buttons should open the same 'Contact Form' modal.

Clicking on any of the buttons indeed opens the modal without a problem. However, when we close the modal - either by clicking on the 'close' button inside the modal, or by hitting 'Esc' on the keyboard - the page automatically scrolls to the position of the last button on the page that is a trigger for the modal. It seems that on 'close' the modal is forcing the viewport to scroll to the last trigger in the DOM!

Obviously, this is unwanted behaviour - as most of the times the visitor is not going to be opening the modal by clicking on the very last button...

This problem is illustrated this CodePen: https://codepen.io/icouto/pen/QgJzoJ

Code summary:

<!-- first trigger button -->
<p><button id="btn1" class="button" data-open="exampleModal1">Click me for a modal</button></p>

<!-- lots of filler text to make the page long -->
<p>lorem ipsum dolor sit amet, etc. etc. etc. </p>

<!-- second trigger button -->
<p><button id="btn2" class="button" data-open="exampleModal1">Click me for a modal</button></p>

<!-- modal window -->
<div class="reveal" id="exampleModal1" data-reveal>
  <h1>Awesome. I Have It.</h1>
  <p class="lead">Your couch. It is mine.</p>
  <p>I'm a cool paragraph that lives inside of an even cooler modal. Wins!</p>
  <button class="close-button" data-close aria-label="Close modal" type="button">
    <span aria-hidden="true">&times;</span>
  </button>
</div>

If you click the top button to open the modal, then close the modal, you will automatically be taken to the bottom of the page...

Am I doing something obviously wrong? Is there something I've missed? Is there a workaround for this that doesn't involve placing multiple copies of the modal, next to every trigger?

Any guidance is appreciated!

Lux Logica
  • 1,429
  • 1
  • 14
  • 30

2 Answers2

8

@Thomas Jefferson's explanation as for why this is happening is correct, but his solution is unsatisfactory. To overcome this you will have to manually open the Modal yourself.

Replace class="button" data-open="exampleModal1" with class="button trigger-example-modal".

Then add this javascript:

$('.trigger-example-modal').on('click', function() {
  $('#exampleModal1').foundation('open');
});
Jamie Chong
  • 832
  • 9
  • 13
  • The bug still exists today :( Your solution worked thanks. – Dan. Dec 20 '17 at 15:38
  • I have the opposite problem. With Foundation 6.5.3, it scrolls to the _first_ trigger even when I manually use 'open' and 'close'. – Trip Jun 24 '19 at 17:44
  • My problem appears to be in _enableScroll(). `$(window).scrollTop()` scrolls to the top no matter what the input is. I've tested on other pages, and it works fine. I guess my page has a weird DOM that breaks it. – Trip Jun 24 '19 at 18:06
2

I had this same issue and couldn't find any fixes on the zurb site.

The problem seems to be that the close method of the reveal object tries to set the focus back on the anchor that opened it (this.$anchor.focus() on last line):

{
        key: "close",
        value: function() {
            function t() {
                e.isMobile ? (0 === u()(".reveal:visible").length && u()("html, body").removeClass("is-reveal-open"),
                e.originalScrollPos && (u()("body").scrollTop(e.originalScrollPos),
                e.originalScrollPos = null)) : 0 === u()(".reveal:visible").length && u()("body").removeClass("is-reveal-open"),
                d.a.releaseFocus(e.$element),
                e.$element.attr("aria-hidden", !0),
                e.$element.trigger("closed.zf.reveal")
            }
            if (!this.isActive || !this.$element.is(":visible"))
                return !1;
            var e = this;
            this.options.animationOut ? (this.options.overlay && f.a.animateOut(this.$overlay, "fade-out"),
            f.a.animateOut(this.$element, this.options.animationOut, t)) : (this.$element.hide(this.options.hideDelay),
            this.options.overlay ? this.$overlay.hide(0, t) : t()),
            this.options.closeOnEsc && u()(window).off("keydown.zf.reveal"),
            !this.options.overlay && this.options.closeOnClick && u()("body").off("click.zf.reveal"),
            this.$element.off("keydown.zf.reveal"),
            this.options.resetOnClose && this.$element.html(this.$element.html()),
            this.isActive = !1,
            e.options.deepLink && (window.history.replaceState ? window.history.replaceState("", document.title, window.location.href.replace("#" + this.id, "")) : window.location.hash = ""),
            this.$anchor.focus()
        }
    }

But $anchor just gets set to an array of all elements with the data-toggle/data-open triggers for that reveal:

this.$anchor = u()('[data-open="' + this.id + '"]').length ? u()('[data-open="' + this.id + '"]') : u()('[data-toggle="' + this.id + '"]'), 

This results in $anchor.focus() focussing on the last element with the respective data-toggle or data-open trigger.

I just commented out this.$anchor.focus() from the close method and it works fine for me now.

  • Your explanation for why is great, but we shouldn't be modifying library code or in the case of Foundation via CDN, it's not even possible to do so. – Jamie Chong Sep 14 '17 at 21:42