PageSourceSearch

https://www.phoenix-agentur-blog.de/wp-content/themes/phoenix-blog…/js/foundation.offcanvas.js?ver=6.1.1

js phoenix-agentur-blog.de collected 2026-10-10 01:28:22 UTC 10,810 bytes, 371 lines download raw bytes

1/**
2 * OffCanvas module.
3 * @module foundation.offcanvas
4 * @requires foundation.util.triggers
5 * @requires foundation.util.motion
6 */
7!function($, Foundation) {
8
9'use strict';
10
11/**
12 * Creates a new instance of an off-canvas wrapper.
13 * @class
14 * @fires OffCanvas#init
15 * @param {Object} element - jQuery object to initialize.
16 * @param {Object} options - Overrides to the default plugin settings.
17 */
18function OffCanvas(element, options) {
19  this.$element = element;
20  this.options = $.extend({}, OffCanvas.defaults, this.$element.data(), options);
21  this.$lastTrigger = $();
22
23  this._init();
24  this._events();
25
26  Foundation.registerPlugin(this, 'OffCanvas');
27}
28
29OffCanvas.defaults = {
30  /**
31   * Allow the user to click outside of the menu to close it.
32   * @option
33   * @example true
34   */
35  closeOnClick: true,
36  /**
37   * Amount of time in ms the open and close transition requires. If none selected, pulls from body style.
38   * @option
39   * @example 500
40   */
41  transitionTime: 0,
42  /**
43   * Direction the offcanvas opens from. Determines class applied to body.
44   * @option
45   * @example left
46   */
47  position: 'left',
48  /**
49   * Force the page to scroll to top on open.
50   */
51  forceTop: true,
52  /**
53   * Allow the offcanvas to be sticky while open. Does nothing if Sass option `$maincontent-prevent-scroll === true`.
54   * Performance in Safari OSX/iOS is not great.
55   */
56  // isSticky: false,
57  /**
58   * Allow the offcanvas to remain open for certain breakpoints. Can be used with `isSticky`.
59   * @option
60   * @example false
61   */
62  isRevealed: false,
63  /**
64   * Breakpoint at which to reveal. JS will use a RegExp to target standard classes, if changing classnames, pass your class @`revealClass`.
65   * @option
66   * @example reveal-for-large
67   */
68  revealOn: null,
69  /**
70   * Force focus to the offcanvas on open. If true, will focus the opening trigger on close.
71   * @option
72   * @example true
73   */
74  autoFocus: true,
75  /**
76   * Class used to force an offcanvas to remain open. Foundation defaults for this are `reveal-for-large` & `reveal-for-medium`.
77   * @option
78   * TODO improve the regex testing for this.
79   * @example reveal-for-large
80   */
81  revealClass: 'reveal-for-',
82  /**
83   * Triggers optional focus trapping when opening an offcanvas. Sets tabindex of [data-off-canvas-content] to -1 for accessibility purposes.
84   * @option
85   * @example true
86   */
87  trapFocus: false
88};
89
90/**
91 * Initializes the off-canvas wrapper by adding the exit overlay (if needed).
92 * @function
93 * @private
94 */
95OffCanvas.prototype._init = function() {
96  var id = this.$element.attr('id');
97
98  this.$element.attr('aria-hidden', 'true');
99
100  // Find triggers that affect this element and add aria-expanded to them
101  $(document)
102    .find('[data-open="'+id+'"], [data-close="'+id+'"], [data-toggle="'+id+'"]')
103    .attr('aria-expanded', 'false')
104    .attr('aria-controls', id);
105
106  // Add a close trigger over the body if necessary
107  if (this.options.closeOnClick){
108    if($('.js-off-canvas-exit').length){
109      this.$exiter = $('.js-off-canvas-exit');
110    }else{
111      var exiter = document.createElement('div');
112      exiter.setAttribute('class', 'js-off-canvas-exit');
113      $('[data-off-canvas-content]').append(exiter);
114
115      this.$exiter = $(exiter);
116    }
117  }
118
119  this.options.isRevealed = this.options.isRevealed || new RegExp(this.options.revealClass, 'g').test(this.$element[0].className);
120
121  if(this.options.isRevealed){
122    this.options.revealOn = this.options.revealOn || this.$element[0].className.match(/(reveal-for-medium|reveal-for-large)/g)[0].split('-')[2];
123    this._setMQChecker();
124  }
125  if(!this.options.transitionTime){
126    this.options.transitionTime = parseFloat(window.getComputedStyle($('[data-off-canvas-wrapper]')[0]).transitionDuration) * 1000;
127  }
128};
129
130/**
131 * Adds event handlers to the off-canvas wrapper and the exit overlay.
132 * @function
133 * @private
134 */
135OffCanvas.prototype._events = function() {
136  this.$element.off('.zf.trigger .zf.offcanvas').on({
137    'open.zf.trigger': this.open.bind(this),
138    'close.zf.trigger': this.close.bind(this),
139    'toggle.zf.trigger': this.toggle.bind(this),
140    'keydown.zf.offcanvas': this._handleKeyboard.bind(this)
141  });
142
143  if (this.$exiter.length) {
144    var _this = this;
145    this.$exiter.on({'click.zf.offcanvas': this.close.bind(this)});
146  }
147};
148/**
149 * Applies event listener for elements that will reveal at certain breakpoints.
150 * @private
151 */
152OffCanvas.prototype._setMQChecker = function(){
153  var _this = this;
154
155  $(window).on('changed.zf.mediaquery', function(){
156    if(Foundation.MediaQuery.atLeast(_this.options.revealOn)){
157      _this.reveal(true);
158    }else{
159      _this.reveal(false);
160    }
161  }).one('load.zf.offcanvas', function(){
162    if(Foundation.MediaQuery.atLeast(_this.options.revealOn)){
163      _this.reveal(true);
164    }
165  });
166};
167/**
168 * Handles the revealing/hiding the off-canvas at breakpoints, not the same as open.
169 * @param {Boolean} isRevealed - true if element should be revealed.
170 * @function
171 */
172OffCanvas.prototype.reveal = function(isRevealed){
173  var $closer = this.$element.find('[data-close]');
174  if(isRevealed){
175    this.close();
176    this.isRevealed = true;
177    // if(!this.options.forceTop){
178    //   var scrollPos = parseInt(window.pageYOffset);
179    //   this.$element[0].style.transform = 'translate(0,' + scrollPos + 'px)';
180    // }
181    // if(this.options.isSticky){ this._stick(); }
182    this.$element.off('open.zf.trigger toggle.zf.trigger');
183    if($closer.length){ $closer.hide(); }
184  }else{
185    this.isRevealed = false;
186    // if(this.options.isSticky || !this.options.forceTop){
187    //   this.$element[0].style.transform = '';
188    //   $(window).off('scroll.zf.offcanvas');
189    // }
190    this.$element.on({
191      'open.zf.trigger': this.open.bind(this),
192      'toggle.zf.trigger': this.toggle.bind(this)
193    });
194    if($closer.length){
195      $closer.show();
196    }
197  }
198};
199
200/**
201 * Opens the off-canvas menu.
202 * @function
203 * @param {Object} event - Event object passed from listener.
204 * @param {jQuery} trigger - element that triggered the off-canvas to open.
205 * @fires OffCanvas#opened
206 */
207OffCanvas.prototype.open = function(event, trigger) {
208  if (this.$element.hasClass('is-open') || this.isRevealed){ return; }
209  var _this = this,
210      $body = $(document.body);
211  $('body').scrollTop(0);
212  // window.pageYOffset = 0;
213
214  // if(!this.options.forceTop){
215  //   var scrollPos = parseInt(window.pageYOffset);
216  //   this.$element[0].style.transform = 'translate(0,' + scrollPos + 'px)';
217  //   if(this.$exiter.length){
218  //     this.$exiter[0].style.transform = 'translate(0,' + scrollPos + 'px)';
219  //   }
220  // }
221  /**
222   * Fires when the off-canvas menu opens.
223   * @event OffCanvas#opened
224   */
225  Foundation.Move(this.options.transitionTime, this.$element, function(){
226    $('[data-off-canvas-wrapper]').addClass('is-off-canvas-open is-open-'+ _this.options.position);
227
228    _this.$element
229      .addClass('is-open')
230
231    // if(_this.options.isSticky){
232    //   _this._stick();
233    // }
234  });
235  this.$element.attr('aria-hidden', 'false')
236      .trigger('opened.zf.offcanvas');
237
238
239  if(trigger){
240    this.$lastTrigger = trigger.attr('aria-expanded', 'true');
241  }
242  if(this.options.autoFocus){
243    this.$element.one('finished.zf.animate', function(){
244      _this.$element.find('a, button').eq(0).focus();
245    });
246  }
247  if(this.options.trapFocus){
248    $('[data-off-canvas-content]').attr('tabindex', '-1');
249    this._trapFocus();
250  }
251};
252/**
253 * Traps focus within the offcanvas on open.
254 * @private
255 */
256OffCanvas.prototype._trapFocus = function(){
257  var focusable = Foundation.Keyboard.findFocusable(this.$element),
258      first = focusable.eq(0),
259      last = focusable.eq(-1);
260
261  focusable.off('.zf.offcanvas').on('keydown.zf.offcanvas', function(e){
262    if(e.which === 9 || e.keycode === 9){
263      if(e.target === last[0] && !e.shiftKey){
264        e.preventDefault();
265        first.focus();
266      }
267      if(e.target === first[0] && e.shiftKey){
268        e.preventDefault();
269        last.focus();
270      }
271    }
272  });
273};
274/**
275 * Allows the offcanvas to appear sticky utilizing translate properties.
276 * @private
277 */
278// OffCanvas.prototype._stick = function(){
279//   var elStyle = this.$element[0].style;
280//
281//   if(this.options.closeOnClick){
282//     var exitStyle = this.$exiter[0].style;
283//   }
284//
285//   $(window).on('scroll.zf.offcanvas', function(e){
286//     console.log(e);
287//     var pageY = window.pageYOffset;
288//     elStyle.transform = 'translate(0,' + pageY + 'px)';
289//     if(exitStyle !== undefined){ exitStyle.transform = 'translate(0,' + pageY + 'px)'; }
290//   });
291//   // this.$element.trigger('stuck.zf.offcanvas');
292// };
293/**
294 * Closes the off-canvas menu.
295 * @function
296 * @param {Function} cb - optional cb to fire after closure.
297 * @fires OffCanvas#closed
298 */
299OffCanvas.prototype.close = function(cb) {
300  if(!this.$element.hasClass('is-open') || this.isRevealed){ return; }
301
302  var _this = this;
303
304  //  Foundation.Move(this.options.transitionTime, this.$element, function(){
305  $('[data-off-canvas-wrapper]').removeClass('is-off-canvas-open is-open-' + _this.options.position);
306  _this.$element.removeClass('is-open');
307    // Foundation._reflow();
308  // });
309  this.$element.attr('aria-hidden', 'true')
310    /**
311     * Fires when the off-canvas menu opens.
312     * @event OffCanvas#closed
313     */
314      .trigger('closed.zf.offcanvas');
315  // if(_this.options.isSticky || !_this.options.forceTop){
316  //   setTimeout(function(){
317  //     _this.$element[0].style.transform = '';
318  //     $(window).off('scroll.zf.offcanvas');
319  //   }, this.options.transitionTime);
320  // }
321
322  this.$lastTrigger.attr('aria-expanded', 'false');
323  if(this.options.trapFocus){
324    $('[data-off-canvas-content]').removeAttr('tabindex');
325  }
326
327};
328
329/**
330 * Toggles the off-canvas menu open or closed.
331 * @function
332 * @param {Object} event - Event object passed from listener.
333 * @param {jQuery} trigger - element that triggered the off-canvas to open.
334 */
335OffCanvas.prototype.toggle = function(event, trigger) {
336  if (this.$element.hasClass('is-open')) {
337    this.close(event, trigger);
338  }
339  else {
340    this.open(event, trigger);
341  }
342};
343
344/**
345 * 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.
346 * @function
347 * @private
348 */
349OffCanvas.prototype._handleKeyboard = function(event) {
350  if (event.which !== 27) return;
351
352  event.stopPropagation();
353  event.preventDefault();
354  this.close();
355  this.$lastTrigger.focus();
356};
357/**
358 * Destroys the offcanvas plugin.
359 * @function
360 */
361OffCanvas.prototype.destroy = function(){
362  this.close();
363  this.$element.off('.zf.trigger .zf.offcanvas');
364  this.$exiter.off('.zf.offcanvas');
365
366  Foundation.unregisterPlugin(this);
367};
368
369Foundation.plugin(OffCanvas, 'OffCanvas');
370
371}(jQuery, Foundation);

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.