PageSourceSearch

https://lkv.li/packages/sw_all/js/foundation/foundation.offcanvas.…=be477dd6d68c574d71a87bc268be737a97e4e81e

js lkv.li collected 2026-10-02 16:46:10 UTC 13,616 bytes, 437 lines download raw bytes

1'use strict';
2
3var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
4
5function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
6
7!function ($) {
8
9  /**
10   * OffCanvas module.
11   * @module foundation.offcanvas
12   * @requires foundation.util.keyboard
13   * @requires foundation.util.mediaQuery
14   * @requires foundation.util.triggers
15   * @requires foundation.util.motion
16   */
17
18  var OffCanvas = function () {
19    /**
20     * Creates a new instance of an off-canvas wrapper.
21     * @class
22     * @fires OffCanvas#init
23     * @param {Object} element - jQuery object to initialize.
24     * @param {Object} options - Overrides to the default plugin settings.
25     */
26    function OffCanvas(element, options) {
27      _classCallCheck(this, OffCanvas);
28
29      this.$element = element;
30      this.options = $.extend({}, OffCanvas.defaults, this.$element.data(), options);
31      this.$lastTrigger = $();
32      this.$triggers = $();
33
34      this._init();
35      this._events();
36
37      Foundation.registerPlugin(this, 'OffCanvas');
38      Foundation.Keyboard.register('OffCanvas', {
39        'ESCAPE': 'close'
40      });
41    }
42
43    /**
44     * Initializes the off-canvas wrapper by adding the exit overlay (if needed).
45     * @function
46     * @private
47     */
48
49
50    _createClass(OffCanvas, [{
51      key: '_init',
52      value: function _init() {
53        var id = this.$element.attr('id');
54
55        this.$element.attr('aria-hidden', 'true');
56
57        this.$element.addClass('is-transition-' + this.options.transition);
58
59        // Find triggers that affect this element and add aria-expanded to them
60        this.$triggers = $(document).find('[data-open="' + id + '"], [data-close="' + id + '"], [data-toggle="' + id + '"]').attr('aria-expanded', 'false').attr('aria-controls', id);
61
62        // Add an overlay over the content if necessary
63        if (this.options.contentOverlay === true) {
64          var overlay = document.createElement('div');
65          var overlayPosition = $(this.$element).css("position") === 'fixed' ? 'is-overlay-fixed' : 'is-overlay-absolute';
66          overlay.setAttribute('class', 'js-off-canvas-overlay ' + overlayPosition);
67          this.$overlay = $(overlay);
68          if (overlayPosition === 'is-overlay-fixed') {
69            $('body').append(this.$overlay);
70          } else {
71            this.$element.siblings('[data-off-canvas-content]').append(this.$overlay);
72          }
73        }
74
75        this.options.isRevealed = this.options.isRevealed || new RegExp(this.options.revealClass, 'g').test(this.$element[0].className);
76
77        if (this.options.isRevealed === true) {
78          this.options.revealOn = this.options.revealOn || this.$element[0].className.match(/(reveal-for-medium|reveal-for-large)/g)[0].split('-')[2];
79          this._setMQChecker();
80        }
81        if (!this.options.transitionTime === true) {
82          this.options.transitionTime = parseFloat(window.getComputedStyle($('[data-off-canvas]')[0]).transitionDuration) * 1000;
83        }
84      }
85
86      /**
87       * Adds event handlers to the off-canvas wrapper and the exit overlay.
88       * @function
89       * @private
90       */
91
92    }, {
93      key: '_events',
94      value: function _events() {
95        this.$element.off('.zf.trigger .zf.offcanvas').on({
96          'open.zf.trigger': this.open.bind(this),
97          'close.zf.trigger': this.close.bind(this),
98          'toggle.zf.trigger': this.toggle.bind(this),
99          'keydown.zf.offcanvas': this._handleKeyboard.bind(this)
100        });
101
102        if (this.options.closeOnClick === true) {
103          var $target = this.options.contentOverlay ? this.$overlay : $('[data-off-canvas-content]');
104          $target.on({ 'click.zf.offcanvas': this.close.bind(this) });
105        }
106      }
107
108      /**
109       * Applies event listener for elements that will reveal at certain breakpoints.
110       * @private
111       */
112
113    }, {
114      key: '_setMQChecker',
115      value: function _setMQChecker() {
116        var _this = this;
117
118        $(window).on('changed.zf.mediaquery', function () {
119          if (Foundation.MediaQuery.atLeast(_this.options.revealOn)) {
120            _this.reveal(true);
121          } else {
122            _this.reveal(false);
123          }
124        }).one('load.zf.offcanvas', function () {
125          if (Foundation.MediaQuery.atLeast(_this.options.revealOn)) {
126            _this.reveal(true);
127          }
128        });
129      }
130
131      /**
132       * Handles the revealing/hiding the off-canvas at breakpoints, not the same as open.
133       * @param {Boolean} isRevealed - true if element should be revealed.
134       * @function
135       */
136
137    }, {
138      key: 'reveal',
139      value: function reveal(isRevealed) {
140        var $closer = this.$element.find('[data-close]');
141        if (isRevealed) {
142          this.close();
143          this.isRevealed = true;
144          this.$element.attr('aria-hidden', 'false');
145          this.$element.off('open.zf.trigger toggle.zf.trigger');
146          if ($closer.length) {
147            $closer.hide();
148          }
149        } else {
150          this.isRevealed = false;
151          this.$element.attr('aria-hidden', 'true');
152          this.$element.off('open.zf.trigger toggle.zf.trigger').on({
153            'open.zf.trigger': this.open.bind(this),
154            'toggle.zf.trigger': this.toggle.bind(this)
155          });
156          if ($closer.length) {
157            $closer.show();
158          }
159        }
160      }
161
162      /**
163       * Stops scrolling of the body when offcanvas is open on mobile Safari and other troublesome browsers.
164       * @private
165       */
166
167    }, {
168      key: '_stopScrolling',
169      value: function _stopScrolling(event) {
170        return false;
171      }
172
173      /**
174       * Opens the off-canvas menu.
175       * @function
176       * @param {Object} event - Event object passed from listener.
177       * @param {jQuery} trigger - element that triggered the off-canvas to open.
178       * @fires OffCanvas#opened
179       */
180
181    }, {
182      key: 'open',
183      value: function open(event, trigger) {
184        if (this.$element.hasClass('is-open') || this.isRevealed) {
185          return;
186        }
187        var _this = this;
188
189        if (trigger) {
190          this.$lastTrigger = trigger;
191        }
192
193        if (this.options.forceTo === 'top') {
194          window.scrollTo(0, 0);
195        } else if (this.options.forceTo === 'bottom') {
196          window.scrollTo(0, document.body.scrollHeight);
197        }
198
199        /**
200         * Fires when the off-canvas menu opens.
201         * @event OffCanvas#opened
202         */
203        _this.$element.addClass('is-open');
204
205        this.$triggers.attr('aria-expanded', 'true');
206        this.$element.attr('aria-hidden', 'false').trigger('opened.zf.offcanvas');
207
208        // If `contentScroll` is set to false, add class and disable scrolling on touch devices.
209        if (this.options.contentScroll === false) {
210          $('body').addClass('is-off-canvas-open').on('touchmove', this._stopScrolling);
211        }
212
213        if (this.options.contentOverlay === true) {
214          this.$overlay.addClass('is-visible');
215        }
216
217        if (this.options.closeOnClick === true && this.options.contentOverlay === true) {
218          this.$overlay.addClass('is-closable');
219        }
220
221        if (this.options.autoFocus === true) {
222          this.$element.one(Foundation.transitionend(this.$element), function () {
223            var canvasFocus = _this.$element.find('[data-autofocus]');
224            if (canvasFocus.length) {
225              canvasFocus.eq(0).focus();
226            } else {
227              _this.$element.find('a, button').eq(0).focus();
228            }
229          });
230        }
231
232        if (this.options.trapFocus === true) {
233          this.$element.siblings('[data-off-canvas-content]').attr('tabindex', '-1');
234          Foundation.Keyboard.trapFocus(this.$element);
235        }
236      }
237
238      /**
239       * Closes the off-canvas menu.
240       * @function
241       * @param {Function} cb - optional cb to fire after closure.
242       * @fires OffCanvas#closed
243       */
244
245    }, {
246      key: 'close',
247      value: function close(cb) {
248        if (!this.$element.hasClass('is-open') || this.isRevealed) {
249          return;
250        }
251
252        var _this = this;
253
254        _this.$element.removeClass('is-open');
255
256        this.$element.attr('aria-hidden', 'true')
257        /**
258         * Fires when the off-canvas menu opens.
259         * @event OffCanvas#closed
260         */
261            .trigger('closed.zf.offcanvas');
262
263        // If `contentScroll` is set to false, remove class and re-enable scrolling on touch devices.
264        if (this.options.contentScroll === false) {
265          $('body').removeClass('is-off-canvas-open').off('touchmove', this._stopScrolling);
266        }
267
268        if (this.options.contentOverlay === true) {
269          this.$overlay.removeClass('is-visible');
270        }
271
272        if (this.options.closeOnClick === true && this.options.contentOverlay === true) {
273          this.$overlay.removeClass('is-closable');
274        }
275
276        this.$triggers.attr('aria-expanded', 'false');
277
278        if (this.options.trapFocus === true) {
279          this.$element.siblings('[data-off-canvas-content]').removeAttr('tabindex');
280          Foundation.Keyboard.releaseFocus(this.$element);
281        }
282      }
283
284      /**
285       * Toggles the off-canvas menu open or closed.
286       * @function
287       * @param {Object} event - Event object passed from listener.
288       * @param {jQuery} trigger - element that triggered the off-canvas to open.
289       */
290
291    }, {
292      key: 'toggle',
293      value: function toggle(event, trigger) {
294        if (this.$element.hasClass('is-open')) {
295          this.close(event, trigger);
296        } else {
297          this.open(event, trigger);
298        }
299      }
300
301      /**
302       * Handles keyboard input when detected. When the escape key is pressed, the off-canvas menu closes, and focus is restored to the element that opened the menu.
303       * @function
304       * @private
305       */
306
307    }, {
308      key: '_handleKeyboard',
309      value: function _handleKeyboard(e) {
310        var _this2 = this;
311
312        Foundation.Keyboard.handleKey(e, 'OffCanvas', {
313          close: function close() {
314            _this2.close();
315            _this2.$lastTrigger.focus();
316            return true;
317          },
318          handled: function handled() {
319            e.stopPropagation();
320            e.preventDefault();
321          }
322        });
323      }
324
325      /**
326       * Destroys the offcanvas plugin.
327       * @function
328       */
329
330    }, {
331      key: 'destroy',
332      value: function destroy() {
333        this.close();
334        this.$element.off('.zf.trigger .zf.offcanvas');
335        this.$overlay.off('.zf.offcanvas');
336
337        Foundation.unregisterPlugin(this);
338      }
339    }]);
340
341    return OffCanvas;
342  }();
343
344  OffCanvas.defaults = {
345    /**
346     * Allow the user to click outside of the menu to close it.
347     * @option
348     * @type {boolean}
349     * @default true
350     */
351    closeOnClick: true,
352
353    /**
354     * Adds an overlay on top of `[data-off-canvas-content]`.
355     * @option
356     * @type {boolean}
357     * @default true
358     */
359    contentOverlay: true,
360
361    /**
362     * Enable/disable scrolling of the main content when an off canvas panel is open.
363     * @option
364     * @type {boolean}
365     * @default true
366     */
367    contentScroll: true,
368
369    /**
370     * Amount of time in ms the open and close transition requires. If none selected, pulls from body style.
371     * @option
372     * @type {number}
373     * @default 0
374     */
375    transitionTime: 0,
376
377    /**
378     * Type of transition for the offcanvas menu. Options are 'push', 'detached' or 'slide'.
379     * @option
380     * @type {string}
381     * @default push
382     */
383    transition: 'push',
384
385    /**
386     * Force the page to scroll to top or bottom on open.
387     * @option
388     * @type {?string}
389     * @default null
390     */
391    forceTo: null,
392
393    /**
394     * Allow the offcanvas to remain open for certain breakpoints.
395     * @option
396     * @type {boolean}
397     * @default false
398     */
399    isRevealed: false,
400
401    /**
402     * Breakpoint at which to reveal. JS will use a RegExp to target standard classes, if changing classnames, pass your class with the `revealClass` option.
403     * @option
404     * @type {?string}
405     * @default null
406     */
407    revealOn: null,
408
409    /**
410     * Force focus to the offcanvas on open. If true, will focus the opening trigger on close.
411     * @option
412     * @type {boolean}
413     * @default true
414     */
415    autoFocus: true,
416
417    /**
418     * Class used to force an offcanvas to remain open. Foundation defaults for this are `reveal-for-large` & `reveal-for-medium`.
419     * @option
420     * @type {string}
421     * @default reveal-for-
422     * @todo improve the regex testing for this.
423     */
424    revealClass: 'reveal-for-',
425
426    /**
427     * Triggers optional focus trapping when opening an offcanvas. Sets tabindex of [data-off-canvas-content] to -1 for accessibility purposes.
428     * @option
429     * @type {boolean}
430     * @default false
431     */
432    trapFocus: false
433  };
434
435  // Window exports
436  Foundation.plugin(OffCanvas, 'OffCanvas');
437}(jQuery);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.