Custom dropdown focus: call stack size exceeded - javascript

I'm working on my own dropdown plugin built with jquery (slim). The dropdown element itself is a div with tabindex="0".
I'd like the dropdown to work with the browser's focus state: open the dropdown when the element is focused and close it when the element loses focus. Currently I'm getting the following error:
jquery.slim.min.js:2 Uncaught RangeError: Maximum call stack size
exceeded
The code looks like this (removed parts for readability, marked problems):
var plugin = 'dropdown',
defaults = {
onOpened : function() {},
onClosed : function() {}
};
// Constructor
function Dropdown(element, options) {
this.element = element;
this.settings = $.extend({}, defaults, options);
this.init();
}
// Instance
$.extend(Dropdown.prototype, {
init: function() {
var instance = this,
$element = $(instance.element);
// Bind listeners
$element.focus(function(e) {
instance.open();
e.preventDefault();
}).focusout(function() {
instance.close();
}).mousedown(function() {
instance.toggle();
});
},
/**
* Check the state of the dropdown.
*
* #param state
* #returns {*}
*/
is: function(state) {
var $element = $(this.element);
return {
open: function() {
return $element.hasClass('dropdown--open');
},
focused: function() {
return document.activeElement === $element[0];
}
}[state].apply();
},
/**
* Open the dropdown.
*/
open: function() {
var instance = this,
$element = $(instance.element);
if (instance.is('open')) {
return;
}
$element.addClass('dropdown--open');
this.callback(this.settings.onOpened, $element);
},
/**
* Close the dropdown.
*/
close: function() {
var instance = this,
$element = $(this.element);
if ( ! instance.is('open')) {
return;
}
$element.removeClass('dropdown--open');
this.callback(this.settings.onClosed, $element);
},
/**
* Make a callback.
*
* #param callback
* #param $element
*/
callback: function(callback, $element) {
if (callback && typeof callback === 'function') {
callback($element);
}
}
});
I know I'm triggering a (endless) recursive function, but I'm unsure how to tackle this problem.
All help is appreciated!
Edit:
Fixed
;(function($, window, document) {
'use strict';
var plugin = 'dropdown',
defaults = {
onOpened : function() {},
onClosed : function() {}
};
// Constructor
function Dropdown(element, options) {
this.element = element;
this.settings = $.extend({}, defaults, options);
this.init();
}
// Instance
$.extend(Dropdown.prototype, {
init: function() {
var instance = this,
$element = $(instance.element);
// Bind listeners
$element.focus(function(e) {
console.log('opening');
instance.open();
e.preventDefault();
}).focusout(function() {
console.log('closing');
instance.close();
}).mousedown(function() {
console.log('toggling');
instance.toggle();
});
},
/**
* Check the state of the dropdown.
*
* #param state
* #returns {*}
*/
is: function(state) {
var $element = $(this.element);
return {
open: function() {
return $element.hasClass('dropdown--open');
},
empty: function() {
return $element.hasClass('dropdown--empty');
},
focused: function() {
return document.activeElement === $element[0];
}
}[state].apply();
},
/**
* Toggles the dropdown.
*/
toggle: function() {
if (this.is('open')) this.close();
else this.open();
},
/**
* Open the dropdown.
*/
open: function() {
var instance = this,
$element = $(instance.element);
if (instance.is('open')) {
return;
}
$element.addClass('dropdown--open');
this.callback(this.settings.onOpened, $element);
},
/**
* Close the dropdown.
*/
close: function() {
var instance = this,
$element = $(this.element);
if ( ! instance.is('open')) {
return;
}
$element.removeClass('dropdown--open');
this.callback(this.settings.onClosed, $element);
},
/**
* Make a callback.
*
* #param callback
* #param $element
*/
callback: function(callback, $element) {
if (callback && typeof callback === 'function') {
callback($element);
}
}
});
// Plugin definition
$.fn.dropdown = function(options, args) {
return this.each(function() {
if ( ! $ .data(this, plugin)) {
$.data(this, plugin, new Dropdown(this, options));
}
});
};
})(jQuery, window, document);
$('.dropdown').dropdown();
.dropdown {
position: relative;
display: block;
padding: .625rem .8125rem;
padding-right: 2rem;
font-size: 16px;
color: #333;
line-height: 1.125;
outline: 0;
cursor: pointer;
border: 1px solid #d9d9d9;
background-color: #fff;
}
.dropdown.dropdown--open .dropdown__menu {
display: block;
}
.dropdown__menu {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="dropdown" tabindex="0">
<span>Favorite animal</span>
<ul class="dropdown__menu" tabindex="-1">
<li class="dropdown__item">Cats</li>
<li class="dropdown__item">Dogs</li>
<li class="dropdown__item">Monkeys</li>
<li class="dropdown__item">Elephants</li>
</ul>
</div>

So. The Problems:
1) You triggered again and again focus()and focusout() if Dropdown open/close. (You already done this)
2) Use your toggle() function to close/open your dropdown
Your Problem was you have click event which checks is dropdown open, then close. But you have todo this in focusOut().
I edited your fiddle
// Bind listeners
$element.on('click', function(e) {
instance.toggle();
});
3) Update from your comment
Fiddle with changing the values

Related

Uncaught TypeError: Cannot read property 'documentElement' of null at Iframe.initializeIframe [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 1 year ago.
Improve this question
after upgrade Chrome browser to v. 91.0.4472.106, console show this error:
Uncaught TypeError: Cannot read property 'documentElement' of null
at Iframe.initializeIframe (Iframe.js?bust=f74493421b3bb4c9f2ea18198ca25746b5ef8a20:202)
this is content of Iframe.js:
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Module: TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Iframe
* The editor iframe
*/
define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/Walker',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/TYPO3',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/Util/Util',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/DOM/DOM',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/Event',
'TYPO3/CMS/Rtehtmlarea/HTMLArea/Event/KeyMap'],
function (UserAgent, Walker, Typo3, Util, Dom, Event, KeyMap) {
/**
* Editor iframe constructor
*
* #param {Object} config
* #constructor
* #exports TYPO3/CMS/Rtehtmlarea/HTMLArea/Editor/Iframe
*/
var Iframe = function (config) {
Util.apply(this, config);
};
Iframe.prototype = {
/**
* Render the iframe (called by framework rendering)
*
* #param object container: the container into which to insert the iframe (that is the framework)
* #return void
*/
render: function (container) {
this.config = this.getEditor().config;
this.createIframe(container);
if (!this.config.showStatusBar) {
Dom.addClass(this.getEl(), 'noStatusBar');
}
this.initStyleChangeEventListener();
if (UserAgent.isOpera) {
var self = this;
Event.one(this.getEl(), 'load', function (event) { self.initializeIframe(); return true; })
} else {
this.initializeIframe();
}
},
/**
* Get the element to which the iframe is rendered
*/
getEl: function () {
return this.el;
},
/**
* The editor iframe may become hidden with style.display = "none" on some parent div
* This breaks the editor in Firefox: the designMode attribute needs to be reset after the style.display of the container div is reset to "block"
* In all browsers, it breaks the evaluation of the framework dimensions
*/
initStyleChangeEventListener: function () {
if (this.isNested) {
if (typeof MutationObserver === 'function') {
var self = this;
this.mutationObserver = new MutationObserver( function (mutations) { self.onNestedShowMutation(mutations); });
var options = {
attributes: true,
attributeFilter: ['class', 'style']
};
for (var i = this.nestedParentElements.sorted.length; --i >= 0;) {
var nestedElement = document.getElementById(this.nestedParentElements.sorted[i]);
this.mutationObserver.observe(nestedElement, options);
this.mutationObserver.observe(nestedElement.parentNode, options);
}
} else {
this.initMutationEventsListeners();
}
}
},
/**
* When Mutation Observer is not available, listen to DOMAttrModified events
*/
initMutationEventsListeners: function () {
var self = this;
var options = {
delay: 50
};
for (var i = this.nestedParentElements.sorted.length; --i >= 0;) {
var nestedElement = document.getElementById(this.nestedParentElements.sorted[i]);
Event.on(
nestedElement,
'DOMAttrModified',
function (event) { return self.onNestedShow(event); },
options
);
Event.on(
nestedElement.parentNode,
'DOMAttrModified',
function (event) { return self.onNestedShow(event); },
options
);
}
},
/**
* editorId should be set in config
*/
editorId: null,
/**
* Get a reference to the editor
*/
getEditor: function () {
return RTEarea[this.editorId].editor;
},
/**
* Get a reference to the toolbar
*/
getToolbar: function () {
return this.framework.toolbar;
},
/**
* Get a reference to the statusBar
*/
getStatusBar: function () {
return this.framework.statusBar;
},
/**
* Get a reference to a button
*/
getButton: function (buttonId) {
return this.getToolbar().getButton(buttonId);
},
/**
* Flag set to true when the iframe becomes usable for editing
*/
ready: false,
/**
* Create the iframe element at rendering time
*
* #param object container: the container into which to insert the iframe (that is the framework)
* #return void
*/
createIframe: function (container) {
if (this.autoEl && this.autoEl.tag) {
this.el = document.createElement(this.autoEl.tag);
if (this.autoEl.id) {
this.el.setAttribute('id', this.autoEl.id);
}
if (this.autoEl.cls) {
this.el.setAttribute('class', this.autoEl.cls);
}
if (this.autoEl.src) {
this.el.setAttribute('src', this.autoEl.src);
}
this.el = container.appendChild(this.el);
}
},
/**
* Get the content window of the iframe
*/
getIframeWindow: function () {
return this.el.contentWindow ? this.el.contentWindow : this.el.contentDocument;
},
/**
* Proceed to build the iframe document head and ensure style sheets are available after the iframe document becomes available
*/
initializeIframe: function () {
var self = this;
var iframe = this.getEl();
// All browsers
if (!iframe || (!iframe.contentWindow && !iframe.contentDocument)) {
window.setTimeout(function () {
self.initializeIframe();
}, 50);
// All except WebKit
} else if (iframe.contentWindow && !UserAgent.isWebKit && (!iframe.contentWindow.document || !iframe.contentWindow.document.documentElement)) {
window.setTimeout(function () {
self.initializeIframe();
}, 50);
// WebKit
} else if (UserAgent.isWebKit && (!iframe.contentDocument.documentElement || !iframe.contentDocument.body)) {
window.setTimeout(function () {
self.initializeIframe();
}, 50);
} else {
this.document = iframe.contentWindow ? iframe.contentWindow.document : iframe.contentDocument;
this.getEditor().document = this.document;
this.createHead();
// Style the document body
Dom.addClass(this.document.body, 'htmlarea-content-body');
// Start listening to things happening in the iframe
// For some unknown reason, this is too early for Opera
if (!UserAgent.isOpera) {
this.startListening();
}
// Hide the iframe
this.hide();
// Set iframe ready
this.ready = true;
/**
* #event HTMLAreaEventIframeReady
* Fires when the iframe style sheets become accessible
*/
Event.trigger(this, 'HTMLAreaEventIframeReady');
}
},
/**
* Show the iframe
*/
show: function () {
this.getEl().style.display = '';
Event.trigger(this, 'HTMLAreaEventIframeShow');
},
/**
* Hide the iframe
*/
hide: function () {
this.getEl().style.display = 'none';
},
/**
* Build the iframe document head
*/
createHead: function () {
var head = this.document.getElementsByTagName('head')[0];
if (!head) {
head = this.document.createElement('head');
this.document.documentElement.appendChild(head);
}
if (this.config.baseURL) {
var base = this.document.getElementsByTagName('base')[0];
if (!base) {
base = this.document.createElement('base');
base.href = this.config.baseURL;
head.appendChild(base);
}
this.getEditor().appendToLog('HTMLArea.Iframe', 'createHead', 'Iframe baseURL set to: ' + base.href, 'info');
}
var link0 = this.document.getElementsByTagName('link')[0];
if (!link0) {
link0 = this.document.createElement('link');
link0.rel = 'stylesheet';
link0.type = 'text/css';
link0.href = this.config.editedContentStyle;
head.appendChild(link0);
this.getEditor().appendToLog('HTMLArea.Iframe', 'createHead', 'Skin CSS set to: ' + link0.href, 'info');
}
var pageStyle;
for (var i = 0, n = this.config.pageStyle.length; i < n; i++) {
pageStyle = this.config.pageStyle[i];
var link = this.document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = pageStyle;
head.appendChild(link);
this.getEditor().appendToLog('HTMLArea.Iframe', 'createHead', 'Content CSS set to: ' + link.href, 'info');
}
},
/**
* Focus on the iframe
*/
focus: function () {
try {
if (UserAgent.isWebKit) {
this.getEl().focus();
}
this.getEl().contentWindow.focus();
} catch(e) { }
},
/**
* Flag indicating whether the framework is inside a tab or inline element that may be hidden
* Should be set in config
*/
isNested: false,
/**
* All nested tabs and inline levels in the sorting order they were applied
* Should be set in config
*/
nestedParentElements: {},
/**
* Set designMode
*
* #param boolean on: if true set designMode to on, otherwise set to off
*
* #rturn void
*/
setDesignMode: function (on) {
if (on) {
if (!UserAgent.isIE) {
if (UserAgent.isGecko) {
// In Firefox, we can't set designMode when we are in a hidden TYPO3 tab or inline element
if (!this.isNested || Typo3.allElementsAreDisplayed(this.nestedParentElements.sorted)) {
this.document.designMode = 'on';
this.setOptions();
}
} else {
this.document.designMode = 'on';
this.setOptions();
}
}
if (UserAgent.isIE || UserAgent.isWebKit) {
this.document.body.contentEditable = true;
}
} else {
if (!UserAgent.isIE) {
this.document.designMode = 'off';
}
if (UserAgent.isIE || UserAgent.isWebKit) {
this.document.body.contentEditable = false;
}
}
},
/**
* Set editing mode options (if we can... raises exception in Firefox 3)
*
* #return void
*/
setOptions: function () {
if (!UserAgent.isIE) {
try {
if (this.document.queryCommandEnabled('insertBrOnReturn')) {
this.document.execCommand('insertBrOnReturn', false, this.config.disableEnterParagraphs);
}
if (this.document.queryCommandEnabled('styleWithCSS')) {
this.document.execCommand('styleWithCSS', false, this.config.useCSS);
} else if (UserAgent.isGecko && this.document.queryCommandEnabled('useCSS')) {
this.document.execCommand('useCSS', false, !this.config.useCSS);
}
if (UserAgent.isGecko) {
if (this.document.queryCommandEnabled('enableObjectResizing')) {
this.document.execCommand('enableObjectResizing', false, !this.config.disableObjectResizing);
}
if (this.document.queryCommandEnabled('enableInlineTableEditing')) {
this.document.execCommand('enableInlineTableEditing', false, (this.config.buttons.table && this.config.buttons.table.enableHandles) ? true : false);
}
}
} catch(e) {}
}
},
/**
* Mutations handler invoked when an hidden TYPO3 hidden nested tab or inline element is shown
*/
onNestedShowMutation: function (mutations) {
for (var i = mutations.length; --i >= 0;) {
var targetId = mutations[i].target.id;
if (this.nestedParentElements.sorted.indexOf(targetId) !== -1 || this.nestedParentElements.sorted.indexOf(targetId.replace('_div', '_fields')) !== -1) {
this.onNestedShowAction();
}
}
},
/**
* Handler invoked when an hidden TYPO3 hidden nested tab or inline element is shown
*/
onNestedShow: function (event) {
Event.stopEvent(event);
var target = event.target;
var delay = event.data.delay;
var self = this;
window.setTimeout(function () {
var styleEvent = true;
// In older versions of Gecko attrName is not set and referring to it causes a non-catchable crash
if ((UserAgent.isGecko && navigator.productSub > 2007112700) || UserAgent.isOpera || UserAgent.isIE) {
styleEvent = (event.originalEvent.attrName === 'style') || (event.originalEvent.attrName === 'className') || (event.originalEvent.attrName === 'class');
}
if (styleEvent && (self.nestedParentElements.sorted.indexOf(target.id) != -1 || self.nestedParentElements.sorted.indexOf(target.id.replace('_div', '_fields')) != -1)) {
self.onNestedShowAction();
}
}, delay);
return false;
},
/**
* Take action when nested tab or inline element is shown
*/
onNestedShowAction: function () {
// Check if all container nested elements are displayed
if (Typo3.allElementsAreDisplayed(this.nestedParentElements.sorted)) {
if (this.getEditor().getMode() === 'wysiwyg') {
if (UserAgent.isGecko) {
this.setDesignMode(true);
}
Event.trigger(this, 'HTMLAreaEventIframeShow');
} else {
Event.trigger(this.framework.getTextAreaContainer(), 'HTMLAreaEventTextAreaContainerShow');
}
this.getToolbar().update();
}
},
/**
* Instance of DOM walker
*/
htmlRenderer: null,
/**
* Getter for the instance of DOM walker
*/
getHtmlRenderer: function () {
if (!this.htmlRenderer) {
this.htmlRenderer = new Walker({
keepComments: !this.config.htmlRemoveComments,
removeTags: this.config.htmlRemoveTags,
removeTagsAndContents: this.config.htmlRemoveTagsAndContents,
baseUrl: this.config.baseURL
});
}
return this.htmlRenderer;
},
/**
* Get the HTML content of the iframe
*/
getHTML: function () {
return this.getHtmlRenderer().render(this.document.body, false);
},
/**
* Start listening to things happening in the iframe
*/
startListening: function () {
var self = this;
// Create keyMap so that plugins may bind key handlers
this.keyMap = new KeyMap(this.document.documentElement, (UserAgent.isIE || UserAgent.isWebKit) ? 'keydown' : 'keypress');
// Special keys map
this.keyMap.addBinding(
{
key: [Event.DOWN, Event.UP, Event.LEFT, Event.RIGHT],
alt: false,
handler: function (event) { return self.onArrow(event); }
}
);
this.keyMap.addBinding(
{
key: Event.TAB,
ctrl: false,
alt: false,
handler: function (event) { return self.onTab(event); }
}
);
this.keyMap.addBinding(
{
key: Event.SPACE,
ctrl: true,
shift: false,
alt: false,
handler: function (event) { return self.onCtrlSpace(event); }
}
);
if (UserAgent.isGecko || UserAgent.isIE || UserAgent.isWebKit) {
this.keyMap.addBinding(
{
key: [Event.BACKSPACE, Event.DELETE],
alt: false,
handler: function (event) { return self.onBackSpace(event); }
});
}
if (!UserAgent.isIE && !this.config.disableEnterParagraphs) {
this.keyMap.addBinding(
{
key: Event.ENTER,
shift: false,
handler: function (event) { return self.onEnter(event); }
});
}
if (UserAgent.isWebKit) {
this.keyMap.addBinding(
{
key: Event.ENTER,
alt: false,
handler: function (event) { return self.onWebKitEnter(event); }
});
}
// Hot key map (on keydown for all browsers)
var hotKeys = [];
for (var key in this.config.hotKeyList) {
if (key.length === 1) {
hotKeys.push(key);
}
}
// Make hot key map available, even if empty, so that plugins may add bindings
this.hotKeyMap = new KeyMap(this.document.documentElement, 'keydown');
if (hotKeys.length > 0) {
this.hotKeyMap.addBinding({
key: hotKeys,
ctrl: true,
shift: false,
alt: false,
handler: function (event) { return self.onHotKey(event); }
});
}
Event.on(
this.document.documentElement,
(UserAgent.isIE || UserAgent.isWebKit) ? 'keydown' : 'keypress',
function (event) { return self.onAnyKey(event); }
);
Event.on(
this.document.documentElement,
'mouseup',
function (event) { return self.onMouse(event); }
);
Event.on(
this.document.documentElement,
'click',
function (event) { return self.onMouse(event); }
);
if (UserAgent.isGecko) {
Event.on(
this.document.documentElement,
'paste',
function (event) { return self.onPaste(event); }
);
}
Event.on(
this.document.documentElement,
'drop',
function (event) { return self.onDrop(event); }
);
if (UserAgent.isWebKit) {
Event.on(
this.document.body,
'dragend',
function (event) { return self.onDrop(event); }
);
}
},
/**
* Handler for other key events
*/
onAnyKey: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
/**
* #event HTMLAreaEventWordCountChange
* Fires when the word count may have changed
*/
Event.trigger(this, 'HTMLAreaEventWordCountChange', [100]);
if (!event.altKey && !(event.ctrlKey || event.metaKey)) {
var key = Event.getKey(event);
// Detect URL in non-IE browsers
if (!UserAgent.isIE && (key !== Event.ENTER || (event.shiftKey && !UserAgent.isWebKit))) {
this.getEditor().getSelection().detectURL(event);
}
// Handle option+SPACE for Mac users
if (UserAgent.isMac && key === Event.NON_BREAKING_SPACE) {
return this.onOptionSpace(key, event);
}
}
return true;
},
/**
* On any key input event, check if input is currently inhibited
*/
inhibitKeyboardInput: function (event) {
// Inhibit key events while server-based cleaning is being processed
if (this.getEditor().inhibitKeyboardInput) {
Event.stopEvent(event);
return true;
} else {
return false;
}
},
/**
* Handler for mouse events
*/
onMouse: function (event) {
// In WebKit, select the image when it is clicked
if (UserAgent.isWebKit && /^(img)$/i.test(event.target.nodeName) && event.type === 'click') {
this.getEditor().getSelection().selectNode(event.target);
}
this.getToolbar().updateLater(100);
return true;
},
/**
* Handler for paste operations in Gecko
*/
onPaste: function (event) {
// Make src and href urls absolute
if (UserAgent.isGecko) {
var self = this;
window.setTimeout(function () {
Dom.makeUrlsAbsolute(self.getEditor().document.body, self.config.baseURL, self.getHtmlRenderer());
}, 50);
}
return true;
},
/**
* Handler for drag and drop operations
*/
onDrop: function (event) {
var self = this;
// Clean up span elements added by WebKit
if (UserAgent.isWebKit) {
window.setTimeout(function () {
self.getEditor().getDomNode().cleanAppleStyleSpans(self.getEditor().document.body);
}, 50);
}
// Make src url absolute in Firefox
if (UserAgent.isGecko) {
window.setTimeout(function () {
Dom.makeUrlsAbsolute(event.target, self.config.baseURL, self.getHtmlRenderer());
}, 50);
}
this.getToolbar().updateLater(100);
return true;
},
/**
* Handler for UP, DOWN, LEFT and RIGHT arrow keys
*/
onArrow: function (event) {
this.getToolbar().updateLater(100);
return true;
},
/**
* Handler for TAB and SHIFT-TAB keys
*
* If available, BlockElements plugin will handle the TAB key
*/
onTab: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
var keyName = (event.shiftKey ? 'SHIFT-' : '') + 'TAB';
if (this.config.hotKeyList[keyName] && this.config.hotKeyList[keyName].cmd) {
var button = this.getButton(this.config.hotKeyList[keyName].cmd);
if (button) {
Event.stopEvent(event);
/**
* #event HTMLAreaEventHotkey
* Fires when the button hotkey is pressed
*/
Event.trigger(button, 'HTMLAreaEventHotkey', [keyName, event]);
return false;
}
}
return true;
},
/**
* Handler for BACKSPACE and DELETE keys
*/
onBackSpace: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
if ((!UserAgent.isIE && !event.shiftKey) || UserAgent.isIE) {
if (this.getEditor().getSelection().handleBackSpace()) {
Event.stopEvent(event);
return false;
}
}
// Update the toolbar state after some time
this.getToolbar().updateLater(200);
return true;
},
/**
* Handler for ENTER key in non-IE browsers
*/
onEnter: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
this.getEditor().getSelection().detectURL(event);
if (this.getEditor().getSelection().checkInsertParagraph()) {
Event.stopEvent(event);
// Update the toolbar state after some time
this.getToolbar().updateLater(200);
return false;
}
// Update the toolbar state after some time
this.getToolbar().updateLater(200);
return true;
},
/**
* Handler for ENTER key in WebKit browsers
*/
onWebKitEnter: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
if (event.shiftKey || this.config.disableEnterParagraphs) {
var editor = this.getEditor();
editor.getSelection().detectURL(event);
if (UserAgent.isSafari) {
var brNode = editor.document.createElement('br');
editor.getSelection().insertNode(brNode);
brNode.parentNode.normalize();
// Selection issue when an URL was detected
if (editor._unlinkOnUndo) {
brNode = brNode.parentNode.parentNode.insertBefore(brNode, brNode.parentNode.nextSibling);
}
if (!brNode.nextSibling || !/\S+/i.test(brNode.nextSibling.textContent)) {
var secondBrNode = editor.document.createElement('br');
secondBrNode = brNode.parentNode.appendChild(secondBrNode);
}
editor.getSelection().selectNode(brNode, false);
Event.stopEvent(event);
// Update the toolbar state after some time
this.getToolbar().updateLater(200);
return false;
}
}
// Update the toolbar state after some time
this.getToolbar().updateLater(200);
return true;
},
/**
* Handler for CTRL-SPACE keys
*/
onCtrlSpace: function (event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
this.getEditor().getSelection().insertHtml(' ');
Event.stopEvent(event);
return false;
},
/**
* Handler for OPTION-SPACE keys on Mac
*/
onOptionSpace: function (key, event) {
if (this.inhibitKeyboardInput(event)) {
return false;
}
this.getEditor().getSelection().insertHtml(' ');
Event.stopEvent(event);
return false;
},
/**
* Handler for configured hotkeys
*/
onHotKey: function (event) {
var key = Event.getKey(event);
if (this.inhibitKeyboardInput(event)) {
return false;
}
var hotKey = String.fromCharCode(key).toLowerCase();
/**
* #event HTMLAreaEventHotkey
* Fires when the button hotkey is pressed
*/
Event.trigger(this.getButton(this.config.hotKeyList[hotKey].cmd), 'HTMLAreaEventHotkey', [hotKey, event]);
return false;
},
/**
* Cleanup (called by framework)
*/
onBeforeDestroy: function () {
// Remove listeners on nested elements
if (this.isNested) {
if (this.mutationObserver) {
this.mutationObserver.disconnect();
} else {
for (var i = this.nestedParentElements.sorted.length; --i >= 0;) {
var nestedElement = document.getElementById(this.nestedParentElements.sorted[i]);
Event.off(nestedElement);
Event.off(nestedElement.parentNode);
}
}
}
Event.off(this);
Event.off(this.getEl());
Event.off(this.document.body);
Event.off(this.document.documentElement);
// Cleaning references to DOM in order to avoid IE memory leaks
this.document = null;
this.el = null;
}
};
return Iframe;
});
error on line 202 by documentElement || !iframe.contentDocument.body)) {:
} else if (UserAgent.isWebKit && (!iframe.contentDocument.documentElement || !iframe.contentDocument.body)) {
How i can fix it ?
I had the same problem and found the bugfix. In old versions, the iframe source was initialized with about:blank in the rtehtmlarea extension, this is the root cause for the problem you are facing. There was an official bugfix 2 years ago - link to Github: https://github.com/FriendsOfTYPO3/rtehtmlarea/commit/a20e23445ca760ba94ed06dca05266b6e22a25fb
You can backport the fix or update the extension, then it is working as expected in the new Chrome version.
Apparently you're using a TYPO3 version prior to v8.7 with rtehtmlarea which is outdated and unsupported since quite a while. With TYPO3 v8.7 ckeditor was introduced.
Maybe this StackOverflow answer helps you a little bit. Otherwise just use Firefox.
But you're strongly advised to upgrade your TYPO3 installation to the latest version v10.4.

Occasional error with div-background-switcher in Firefox

So I have a div where I change the background images with a script. I simplified everything here: https://inmeditas.satsang-hamburg.de/test.html
Unfortunately about 10% of the time I get an error in Firefox which looks like this:
So far everything seems to work fine in Chrome and other browsers. Here is the code as snippet:
/*!
* jQuery.BgSwitcher
*
* #version 0.4.3
* #author rewish <rewish.org#gmail.com>
* #license MIT License (https://github.com/rewish/jquery-bgswitcher/blob/master/LICENSE.md)
* #link https://github.com/rewish/jquery-bgswitcher
*/
(function($) {
'use strict';
var loadedImages = {},
slice = Array.prototype.slice,
toString = Object.prototype.toString,
corners = ['Top', 'Right', 'Bottom', 'Left'],
backgroundProperties = [
'Attachment', 'Color', 'Image', 'Repeat',
'Position', 'Size', 'Clip', 'Origin'
];
$.fn.bgswitcher = function() {
var args = arguments,
instanceKey = BgSwitcher.keys.instance;
return this.each(function() {
var instance = $.data(this, instanceKey);
if (!instance) {
instance = new BgSwitcher(this);
$.data(this, instanceKey, instance);
}
instance.dispatch.apply(instance, args);
});
};
// Backward Compatibility
$.fn.bgSwitcher = $.fn.bgswitcher;
/**
* BgSwitcher
*
* #param {HTMLElement} el
* #constructor
*/
function BgSwitcher(el) {
this.$el = $(el);
this.index = 0;
this.config = $.extend({}, BgSwitcher.defaultConfig);
this._setupBackgroundElement();
this._listenToResize();
}
$.extend(BgSwitcher.prototype, {
/**
* Dispatch
*
* #param {string|Array} one
*/
dispatch: function(one) {
switch (toString.call(one)) {
case '[object Object]':
this.setConfig(one);
break;
case '[object String]':
this[one].apply(this, slice.call(arguments, 1));
break;
default:
throw new Error('Please specify a Object or String');
}
},
/**
* Set config
*
* #param {Object} config
*/
setConfig: function(config) {
this.config = $.extend(this.config, config);
if (typeof this.config.random !== 'undefined') {
this.config.shuffle = this.config.random;
}
this.refresh();
},
/**
* Set images
*
* #param {Array} images
*/
setImages: function(images) {
this.imageList = new this.constructor.ImageList(images);
if (this.config.shuffle) {
this.imageList.shuffle();
}
},
/**
* Set switch handler
*
* #param {Function} fn
*/
setSwitchHandler: function(fn) {
this.switchHandler = $.proxy(fn, this);
},
/**
* Default switch handler
*
* #param {string} type
* #returns {Function}
*/
getBuiltInSwitchHandler: function(type) {
return this.constructor.switchHandlers[type || this.config.effect];
},
/**
* Refresh
*/
refresh: function() {
this.setImages(this.config.images);
this.setSwitchHandler(this.getBuiltInSwitchHandler());
this._prepareSwitching();
if (this.config.start) {
this.start();
}
},
/**
* Start switching
*/
start: function() {
if (!this._timerID) {
this._timerID = setTimeout($.proxy(this, 'next'), this.config.interval);
}
},
/**
* Stop switching
*/
stop: function() {
if (this._timerID) {
clearTimeout(this._timerID);
this._timerID = null;
}
},
/**
* Toggle between start/stop
*/
toggle: function() {
if (this._timerID) {
this.stop();
} else {
this.start();
}
},
/**
* Reset switching
*/
reset: function() {
this.index = 0;
this._prepareSwitching();
},
/**
* Go to next switching
*/
next: function() {
var max = this.imageList.count();
if (!this.config.loop && this.index + 1 === max) {
return;
}
if (++this.index === max) {
this.index = 0;
}
this.switching();
},
/**
* Go to previous switching
*/
prev: function() {
if (!this.config.loop && this.index === 0) {
return;
}
if (--this.index === -1) {
this.index = this.imageList.count() - 1;
}
this.switching();
},
/**
* Select the switching at index
*
* #param {number} index
*/
select: function(index) {
if (index === -1) {
index = this.imageList.count() - 1;
}
this.index = index;
this.switching();
},
/**
* Switching the background image
*/
switching: function() {
var started = !!this._timerID;
if (started) {
this.stop();
}
this._createSwitchableElement();
this._prepareSwitching();
this.switchHandler(this.$switchable);
if (started) {
this.start();
}
},
/**
* Destroy...
*/
destroy: function() {
this.stop();
this._stopListeningToResize();
if (this.$switchable) {
this.$switchable.stop();
this.$switchable.remove();
this.$switchable = null;
}
if (this.$bg) {
this.$bg.remove();
this.$bg = null;
}
this.$el.removeAttr('style');
this.$el.removeData(this.constructor.keys.instance);
this.$el = null;
},
/**
* Adjust rectangle
*/
_adjustRectangle: function() {
var corner,
i = 0,
length = corners.length,
offset = this.$el.position(),
copiedStyles = {
top: offset.top,
left: offset.left,
width: this.$el.innerWidth(),
height: this.$el.innerHeight()
};
for (; i < length; i++) {
corner = corners[i];
copiedStyles['margin' + corner] = this.$el.css('margin' + corner);
copiedStyles['border' + corner] = this.$el.css('border' + corner);
}
this.$bg.css(copiedStyles);
},
/**
* Setup background element
*/
_setupBackgroundElement: function() {
this.$bg = $(document.createElement('div'));
this.$bg.css({
position: 'absolute',
zIndex: (parseInt(this.$el.css('zIndex'), 10) || 0) - 1,
overflow: 'hidden'
});
this._copyBackgroundStyles();
this._adjustRectangle();
if (this.$el[0].tagName === 'BODY') {
this.$el.prepend(this.$bg);
} else {
this.$el.before(this.$bg);
this.$el.css('background', 'none');
}
},
/**
* Create switchable element
*/
_createSwitchableElement: function() {
if (this.$switchable) {
this.$switchable.remove();
}
this.$switchable = this.$bg.clone();
this.$switchable.css({top: 0, left: 0, margin: 0, border: 'none'});
this.$switchable.appendTo(this.$bg);
},
/**
* Copy background styles
*/
_copyBackgroundStyles: function () {
var prop,
copiedStyle = {},
i = 0,
length = backgroundProperties.length,
backgroundPosition = 'backgroundPosition';
for (; i < length; i++) {
prop = 'background' + backgroundProperties[i];
copiedStyle[prop] = this.$el.css(prop);
}
// For IE<=9
if (copiedStyle[backgroundPosition] === undefined) {
copiedStyle[backgroundPosition] = [
this.$el.css(backgroundPosition + 'X'),
this.$el.css(backgroundPosition + 'Y')
].join(' ');
}
this.$bg.css(copiedStyle);
},
/**
* Listen to the resize event
*/
_listenToResize: function() {
var that = this;
this._resizeHandler = function() {
that._adjustRectangle();
};
$(window).on('resize', this._resizeHandler);
},
/**
* Stop listening to the resize event
*/
_stopListeningToResize: function() {
$(window).off('resize', this._resizeHandler);
this._resizeHandler = null;
},
/**
* Prepare the Switching
*/
_prepareSwitching: function() {
this.$bg.css('backgroundImage', this.imageList.url(this.index));
}
});
/**
* Data Keys
* #type {Object}
*/
BgSwitcher.keys = {
instance: 'bgSwitcher'
};
/**
* Default Config
* #type {Object}
*/
BgSwitcher.defaultConfig = {
images: [],
interval: 5000,
start: true,
loop: true,
shuffle: false,
effect: 'fade',
duration: 1000,
easing: 'swing'
};
/**
* Built-In switch handlers (effects)
* #type {Object}
*/
BgSwitcher.switchHandlers = {
fade: function($el) {
$el.animate({opacity: 0}, this.config.duration, this.config.easing);
},
blind: function($el) {
$el.animate({height: 0}, this.config.duration, this.config.easing);
},
clip: function($el) {
$el.animate({
top: parseInt($el.css('top'), 10) + $el.height() / 2,
height: 0
}, this.config.duration, this.config.easing);
},
slide: function($el) {
$el.animate({top: -$el.height()}, this.config.duration, this.config.easing);
},
drop: function($el) {
$el.animate({
left: -$el.width(),
opacity: 0
}, this.config.duration, this.config.easing);
},
hide: function($el) {
$el.hide();
}
};
/**
* Define effect
*
* #param {String} name
* #param {Function} fn
*/
BgSwitcher.defineEffect = function(name, fn) {
this.switchHandlers[name] = fn;
};
/**
* BgSwitcher.ImageList
*
* #param {Array} images
* #constructor
*/
BgSwitcher.ImageList = function(images) {
this.images = images;
this.createImagesBySequence();
this.preload();
};
$.extend(BgSwitcher.ImageList.prototype, {
/**
* Images is sequenceable
*
* #returns {boolean}
*/
isSequenceable: function() {
return typeof this.images[0] === 'string' &&
typeof this.images[1] === 'number' &&
typeof this.images[2] === 'number';
},
/**
* Create an images by sequence
*/
createImagesBySequence: function() {
if (!this.isSequenceable()) {
return;
}
var images = [],
base = this.images[0],
min = this.images[1],
max = this.images[2];
do {
images.push(base.replace(/\.\w+$/, min + '$&'));
} while (++min <= max);
this.images = images;
},
/**
* Preload an images
*/
preload: function() {
var path,
length = this.images.length,
i = 0;
for (; i < length; i++) {
path = this.images[i];
if (!loadedImages[path]) {
loadedImages[path] = new Image();
loadedImages[path].src = path;
}
}
},
/**
* Shuffle an images
*/
shuffle: function() {
var j, t,
i = this.images.length,
original = this.images.join();
if (!i) {
return;
}
while (i) {
j = Math.floor(Math.random() * i);
t = this.images[--i];
this.images[i] = this.images[j];
this.images[j] = t;
}
if (this.images.join() === original) {
this.shuffle();
}
},
/**
* Get the image from index
*
* #param {number} index
* #returns {string}
*/
get: function(index) {
return this.images[index];
},
/**
* Get the URL with function of CSS
*
* #param {number} index
* #returns {string}
*/
url: function(index) {
return 'url(' + this.get(index) + ')';
},
/**
* Count of images
*
* #returns {number}
*/
count: function() {
return this.images.length;
}
});
$.BgSwitcher = BgSwitcher;
}(jQuery));
$(".amrum").bgswitcher({
images: ["https://inmeditas.satsang-hamburg.de/headerAmrum1.jpg", "https://inmeditas.satsang-hamburg.de/headerAmrum2.jpg", "https://inmeditas.satsang-hamburg.de/headerAmrum3.jpg", "https://inmeditas.satsang-hamburg.de/headerAmrum4.jpg"],
interval: 5000,
duration: 1000
});
.amrum {
background-position: center;
background-size: cover;
}
.unterseite {
width: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="amrum"><img class="unterseite" src="https://inmeditas.satsang-hamburg.de/headerAmrum-hilf.png" /></div>
I would love a solution without that mistake and without problems with the Bootstrap 4 Navbar (which some other scripts unfortunately have that I tried).
Thanks for your help.
I now used http://responsiveslides.com/ and so far it works with Bootstrap 4 and I didn't get an error.
/*! ResponsiveSlides.js v1.55
* http://responsiveslides.com
* http://viljamis.com
*
* Copyright (c) 2011-2012 #viljamis
* Available under the MIT license
*/
/*jslint browser: true, sloppy: true, vars: true, plusplus: true, indent: 2 */
(function ($, window, i) {
$.fn.responsiveSlides = function (options) {
// Default settings
var settings = $.extend({
"auto": true, // Boolean: Animate automatically, true or false
"speed": 1000, // Integer: Speed of the transition, in milliseconds
"timeout": 5000, // Integer: Time between slide transitions, in milliseconds
"pager": false, // Boolean: Show pager, true or false
"nav": false, // Boolean: Show navigation, true or false
"random": false, // Boolean: Randomize the order of the slides, true or false
"pause": false, // Boolean: Pause on hover, true or false
"pauseControls": true, // Boolean: Pause when hovering controls, true or false
"prevText": "Previous", // String: Text for the "previous" button
"nextText": "Next", // String: Text for the "next" button
"maxwidth": "", // Integer: Max-width of the slideshow, in pixels
"navContainer": "", // Selector: Where auto generated controls should be appended to, default is after the <ul>
"manualControls": "", // Selector: Declare custom pager navigation
"namespace": "rslides", // String: change the default namespace used
"before": $.noop, // Function: Before callback
"after": $.noop // Function: After callback
}, options);
return this.each(function () {
// Index for namespacing
i++;
var $this = $(this),
// Local variables
vendor,
selectTab,
startCycle,
restartCycle,
rotate,
$tabs,
// Helpers
index = 0,
$slide = $this.children(),
length = $slide.length,
fadeTime = parseFloat(settings.speed),
waitTime = parseFloat(settings.timeout),
maxw = parseFloat(settings.maxwidth),
// Namespacing
namespace = settings.namespace,
namespaceIdx = namespace + i,
// Classes
navClass = namespace + "_nav " + namespaceIdx + "_nav",
activeClass = namespace + "_here",
visibleClass = namespaceIdx + "_on",
slideClassPrefix = namespaceIdx + "_s",
// Pager
$pager = $("<ul class='" + namespace + "_tabs " + namespaceIdx + "_tabs' />"),
// Styles for visible and hidden slides
visible = {"float": "left", "position": "relative", "opacity": 1, "zIndex": 2},
hidden = {"float": "none", "position": "absolute", "opacity": 0, "zIndex": 1},
// Detect transition support
supportsTransitions = (function () {
var docBody = document.body || document.documentElement;
var styles = docBody.style;
var prop = "transition";
if (typeof styles[prop] === "string") {
return true;
}
// Tests for vendor specific prop
vendor = ["Moz", "Webkit", "Khtml", "O", "ms"];
prop = prop.charAt(0).toUpperCase() + prop.substr(1);
var i;
for (i = 0; i < vendor.length; i++) {
if (typeof styles[vendor[i] + prop] === "string") {
return true;
}
}
return false;
})(),
// Fading animation
slideTo = function (idx) {
settings.before(idx);
// If CSS3 transitions are supported
if (supportsTransitions) {
$slide
.removeClass(visibleClass)
.css(hidden)
.eq(idx)
.addClass(visibleClass)
.css(visible);
index = idx;
setTimeout(function () {
settings.after(idx);
}, fadeTime);
// If not, use jQuery fallback
} else {
$slide
.stop()
.fadeOut(fadeTime, function () {
$(this)
.removeClass(visibleClass)
.css(hidden)
.css("opacity", 1);
})
.eq(idx)
.fadeIn(fadeTime, function () {
$(this)
.addClass(visibleClass)
.css(visible);
settings.after(idx);
index = idx;
});
}
};
// Random order
if (settings.random) {
$slide.sort(function () {
return (Math.round(Math.random()) - 0.5);
});
$this
.empty()
.append($slide);
}
// Add ID's to each slide
$slide.each(function (i) {
this.id = slideClassPrefix + i;
});
// Add max-width and classes
$this.addClass(namespace + " " + namespaceIdx);
if (options && options.maxwidth) {
$this.css("max-width", maxw);
}
// Hide all slides, then show first one
$slide
.hide()
.css(hidden)
.eq(0)
.addClass(visibleClass)
.css(visible)
.show();
// CSS transitions
if (supportsTransitions) {
$slide
.show()
.css({
// -ms prefix isn't needed as IE10 uses prefix free version
"-webkit-transition": "opacity " + fadeTime + "ms ease-in-out",
"-moz-transition": "opacity " + fadeTime + "ms ease-in-out",
"-o-transition": "opacity " + fadeTime + "ms ease-in-out",
"transition": "opacity " + fadeTime + "ms ease-in-out"
});
}
// Only run if there's more than one slide
if ($slide.length > 1) {
// Make sure the timeout is at least 100ms longer than the fade
if (waitTime < fadeTime + 100) {
return;
}
// Pager
if (settings.pager && !settings.manualControls) {
var tabMarkup = [];
$slide.each(function (i) {
var n = i + 1;
tabMarkup +=
"<li>" +
"<a href='#' class='" + slideClassPrefix + n + "'>" + n + "</a>" +
"</li>";
});
$pager.append(tabMarkup);
// Inject pager
if (options.navContainer) {
$(settings.navContainer).append($pager);
} else {
$this.after($pager);
}
}
// Manual pager controls
if (settings.manualControls) {
$pager = $(settings.manualControls);
$pager.addClass(namespace + "_tabs " + namespaceIdx + "_tabs");
}
// Add pager slide class prefixes
if (settings.pager || settings.manualControls) {
$pager.find('li').each(function (i) {
$(this).addClass(slideClassPrefix + (i + 1));
});
}
// If we have a pager, we need to set up the selectTab function
if (settings.pager || settings.manualControls) {
$tabs = $pager.find('a');
// Select pager item
selectTab = function (idx) {
$tabs
.closest("li")
.removeClass(activeClass)
.eq(idx)
.addClass(activeClass);
};
}
// Auto cycle
if (settings.auto) {
startCycle = function () {
rotate = setInterval(function () {
// Clear the event queue
$slide.stop(true, true);
var idx = index + 1 < length ? index + 1 : 0;
// Remove active state and set new if pager is set
if (settings.pager || settings.manualControls) {
selectTab(idx);
}
slideTo(idx);
}, waitTime);
};
// Init cycle
startCycle();
}
// Restarting cycle
restartCycle = function () {
if (settings.auto) {
// Stop
clearInterval(rotate);
// Restart
startCycle();
}
};
// Pause on hover
if (settings.pause) {
$this.hover(function () {
clearInterval(rotate);
}, function () {
restartCycle();
});
}
// Pager click event handler
if (settings.pager || settings.manualControls) {
$tabs.bind("click", function (e) {
e.preventDefault();
if (!settings.pauseControls) {
restartCycle();
}
// Get index of clicked tab
var idx = $tabs.index(this);
// Break if element is already active or currently animated
if (index === idx || $("." + visibleClass).queue('fx').length) {
return;
}
// Remove active state from old tab and set new one
selectTab(idx);
// Do the animation
slideTo(idx);
})
.eq(0)
.closest("li")
.addClass(activeClass);
// Pause when hovering pager
if (settings.pauseControls) {
$tabs.hover(function () {
clearInterval(rotate);
}, function () {
restartCycle();
});
}
}
// Navigation
if (settings.nav) {
var navMarkup =
"<a href='#' class='" + navClass + " prev'>" + settings.prevText + "</a>" +
"<a href='#' class='" + navClass + " next'>" + settings.nextText + "</a>";
// Inject navigation
if (options.navContainer) {
$(settings.navContainer).append(navMarkup);
} else {
$this.after(navMarkup);
}
var $trigger = $("." + namespaceIdx + "_nav"),
$prev = $trigger.filter(".prev");
// Click event handler
$trigger.bind("click", function (e) {
e.preventDefault();
var $visibleClass = $("." + visibleClass);
// Prevent clicking if currently animated
if ($visibleClass.queue('fx').length) {
return;
}
// Adds active class during slide animation
// $(this)
// .addClass(namespace + "_active")
// .delay(fadeTime)
// .queue(function (next) {
// $(this).removeClass(namespace + "_active");
// next();
// });
// Determine where to slide
var idx = $slide.index($visibleClass),
prevIdx = idx - 1,
nextIdx = idx + 1 < length ? index + 1 : 0;
// Go to slide
slideTo($(this)[0] === $prev[0] ? prevIdx : nextIdx);
if (settings.pager || settings.manualControls) {
selectTab($(this)[0] === $prev[0] ? prevIdx : nextIdx);
}
if (!settings.pauseControls) {
restartCycle();
}
});
// Pause when hovering navigation
if (settings.pauseControls) {
$trigger.hover(function () {
clearInterval(rotate);
}, function () {
restartCycle();
});
}
}
}
// Max-width fallback
if (typeof document.body.style.maxWidth === "undefined" && options.maxwidth) {
var widthSupport = function () {
$this.css("width", "100%");
if ($this.width() > maxw) {
$this.css("width", maxw);
}
};
// Init fallback
widthSupport();
$(window).bind("resize", function () {
widthSupport();
});
}
});
};
})(jQuery, this, 0);
$(function() {
$(".rslides").responsiveSlides();
});
.rslides {
position: relative;
list-style: none;
overflow: hidden;
width: 100%;
padding: 0;
margin: 0;
}
.rslides li {
-webkit-backface-visibility: hidden;
position: absolute;
display: none;
width: 100%;
left: 0;
top: 0;
}
.rslides li:first-child {
position: relative;
display: block;
float: left;
}
.rslides img {
display: block;
height: auto;
float: left;
width: 100%;
border: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="rslides">
<li><img src="https://inmeditas.satsang-hamburg.de/headerAmrum1.jpg" alt=""></li>
<li><img src="https://inmeditas.satsang-hamburg.de/headerAmrum2.jpg" alt=""></li>
<li><img src="https://inmeditas.satsang-hamburg.de/headerAmrum3.jpg" alt=""></li>
<li><img src="https://inmeditas.satsang-hamburg.de/headerAmrum4.jpg" alt=""></li>
</ul>

shuffle.js add 3 compound filters

I need to add third compound filter to this shuffle.js code anyone can help?
// ES7 will have Array.prototype.includes.
function arrayIncludes(array, value) {
return array.indexOf(value) !== -1;
}
// Convert an array-like object to a real array.
function toArray(thing) {
return Array.prototype.slice.call(thing);
}
var Demo = function (element) {
this.shapes = toArray(document.querySelectorAll('.js-shapes input'));
this.colors = toArray(document.querySelectorAll('.js-colors button'));
this.shuffle = new Shuffle(element, {
easing: 'cubic-bezier(0.165, 0.840, 0.440, 1.000)', // easeOutQuart
sizer: '.the-sizer',
});
this.filters = {
shapes: [],
colors: [],
};
this._bindEventListeners();
};
/**
* Bind event listeners for when the filters change.
*/
Demo.prototype._bindEventListeners = function () {
this._onShapeChange = this._handleShapeChange.bind(this);
this._onColorChange = this._handleColorChange.bind(this);
this.shapes.forEach(function (input) {
input.addEventListener('change', this._onShapeChange);
}, this);
this.colors.forEach(function (butt) {
butt.addEventListener('click', this._onColorChange);
}, this);
};
/**
* Get the values of each checked input.
* #return {Array.<string>}
*/
Demo.prototype._getCurrentShapeFilters = function () {
return this.shapes.filter(function (input) {
return input.checked;
}).map(function (input) {
return input.value;
});
};
/**
* Get the values of each `active` button.
* #return {Array.<string>}
*/
Demo.prototype._getCurrentColorFilters = function () {
return this.colors.filter(function (butt) {
return butt.classList.contains('active');
}).map(function (butt) {
return butt.getAttribute('data-value');
});
};
/**
* A shape input check state changed, update the current filters and filte.r
*/
Demo.prototype._handleShapeChange = function () {
this.filters.shapes = this._getCurrentShapeFilters();
this.filter();
};
/**
* A color button was clicked. Update filters and display.
* #param {Event} evt Click event object.
*/
Demo.prototype._handleColorChange = function (evt) {
var butt = evt.currentTarget;
// Treat these buttons like radio buttons where only 1 can be selected.
if (butt.classList.contains('active')) {
butt.classList.remove('active');
} else {
this.colors.forEach(function (bt) {
bt.classList.remove('active');
});
butt.classList.add('active');
}
this.filters.colors = this._getCurrentColorFilters();
this.filter();
};
/**
* Filter shuffle based on the current state of filters.
*/
Demo.prototype.filter = function () {
if (this.hasActiveFilters()) {
this.shuffle.filter(this.itemPassesFilters.bind(this));
} else {
this.shuffle.filter(Shuffle.ALL_ITEMS);
}
};
/**
* If any of the arrays in the `filters` property have a length of more than zero,
* that means there is an active filter.
* #return {boolean}
*/
Demo.prototype.hasActiveFilters = function () {
return Object.keys(this.filters).some(function (key) {
return this.filters[key].length > 0;
}, this);
};
/**
* Determine whether an element passes the current filters.
* #param {Element} element Element to test.
* #return {boolean} Whether it satisfies all current filters.
*/
Demo.prototype.itemPassesFilters = function (element) {
var shapes = this.filters.shapes;
var colors = this.filters.colors;
var shape = element.getAttribute('data-shape');
var color = element.getAttribute('data-color');
// If there are active shape filters and this shape is not in that array.
if (shapes.length > 0 && !arrayIncludes(shapes, shape)) {
return false;
}
// If there are active color filters and this color is not in that array.
if (colors.length > 0 && !arrayIncludes(colors, color)) {
return false;
}
return true;
This code already filter items by shape and color I need also to filter this by group.
<div class= "filter-group col-xs-12 filter-options" style="padding:0">
<div class="col-sm-4 col-xs-12" data-group="full">
<a>
<img src="images/product/one-piece.png" alt=""/>
</a>
</div>
<div class="col-sm-4 col-xs-12" data-group="top">
<a>
<img src="images/product/top.png" alt=""/>
</a>
</div>
<div class="col-sm-4 col-xs-12 bottom" data-group="bottom">
<a>
<img src="images/product/bottom.png" alt=""/>
</a>
</div>
</div>
</div>
If some one clicked to div inside .filter-options, can we make another filter option using data-group="" attribute?
It took some time but I was able to do this. Here is the code.
// ES7 will have Array.prototype.includes.
function arrayIncludes(array, value) {
return array.indexOf(value) !== -1;
}
// Convert an array-like object to a real array.
function toArray(thing) {
return Array.prototype.slice.call(thing);
}
var Demo = function (element) {
this.shapes = toArray(document.querySelectorAll('.js-shapes input'));
this.colors = toArray(document.querySelectorAll('.js-colors button'));
this.styles = toArray(document.querySelectorAll('.filter-group div'));
this.shuffle = new Shuffle(element, {
easing: 'cubic-bezier(0.165, 0.840, 0.440, 1.000)', // easeOutQuart
sizer: '.the-sizer',
});
this.filters = {
shapes: [],
colors: [],
styles: [],
};
this._bindEventListeners();
};
/**
* Bind event listeners for when the filters change.
*/
Demo.prototype._bindEventListeners = function () {
this._onShapeChange = this._handleShapeChange.bind(this);
this._onColorChange = this._handleColorChange.bind(this);
this._onStyleChange = this._handleStyleChange.bind(this);
this.shapes.forEach(function (input) {
input.addEventListener('change', this._onShapeChange);
}, this);
this.colors.forEach(function (button) {
button.addEventListener('click', this._onColorChange);
}, this);
this.styles.forEach(function (div) {
div.addEventListener('click', this._onStyleChange);
}, this);
};
/**
* Get the values of each checked input.
* #return {Array.<string>}
*/
Demo.prototype._getCurrentShapeFilters = function () {
return this.shapes.filter(function (input) {
return input.checked;
}).map(function (input) {
return input.value;
});
};
/**
* Get the values of each `active` button.
* #return {Array.<string>}
*/
Demo.prototype._getCurrentColorFilters = function () {
return this.colors.filter(function (button) {
return button.classList.contains('active');
}).map(function (button) {
return button.getAttribute('data-value');
});
};
Demo.prototype._getCurrentStyleFilters = function () {
return this.styles.filter(function (div) {
return div.classList.contains('active');
}).map(function (div) {
return div.getAttribute('data-group');
});
};
/**
* A shape input check state changed, update the current filters and filte.r
*/
Demo.prototype._handleShapeChange = function () {
this.filters.shapes = this._getCurrentShapeFilters();
this.filter();
};
/**
* A color button was clicked. Update filters and display.
* #param {Event} evt Click event object.
*/
Demo.prototype._handleColorChange = function (evt) {
var button = evt.currentTarget;
// Treat these buttons like radio buttons where only 1 can be selected.
if (button.classList.contains('active')) {
button.classList.remove('active');
} else {
this.colors.forEach(function (btn) {
btn.classList.remove('active');
});
button.classList.add('active');
}
this.filters.colors = this._getCurrentColorFilters();
this.filter();
};
Demo.prototype._handleStyleChange = function (ev) {
var div = ev.currentTarget;
// Treat these buttons like radio buttons where only 1 can be selected.
if (div.classList.contains('active')) {
div.classList.remove('active');
} else {
this.colors.forEach(function (dv) {
//dv.classList.remove('active');
});
div.classList.add('active');
}
this.filters.styles = this._getCurrentStyleFilters();
this.filter();
};
/**
* Filter shuffle based on the current state of filters.
*/
Demo.prototype.filter = function () {
if (this.hasActiveFilters()) {
this.shuffle.filter(this.itemPassesFilters.bind(this));
} else {
this.shuffle.filter(Shuffle.ALL_ITEMS);
}
};
/**
* If any of the arrays in the `filters` property have a length of more than zero,
* that means there is an active filter.
* #return {boolean}
*/
Demo.prototype.hasActiveFilters = function () {
return Object.keys(this.filters).some(function (key) {
return this.filters[key].length > 0;
}, this);
};
/**
* Determine whether an element passes the current filters.
* #param {Element} element Element to test.
* #return {boolean} Whether it satisfies all current filters.
*/
Demo.prototype.itemPassesFilters = function (element) {
var shapes = this.filters.shapes;
var colors = this.filters.colors;
var styles = this.filters.styles;
var shape = element.getAttribute('data-shape');
var color = element.getAttribute('data-color');
var style = element.getAttribute('data-groups');
// If there are active shape filters and this shape is not in that array.
if (shapes.length > 0 && !arrayIncludes(shapes, shape)) {
return false;
}
// If there are active color filters and this color is not in that array.
if (colors.length > 0 && !arrayIncludes(colors, color)) {
return false;
}
// If there are active color filters and this color is not in that array.
if (styles.length > 0 && !arrayIncludes(styles, style)) {
return false;
}
return true;
};

Cannot read property 'wrapup' of undefined console

Hey all I am using flexslider in my website for slider the slider is running fine on my server at http://www.reurl.in/31830875a but when checked on local server or made life somewhere else I am getting this error in console "Cannot read property 'wrapup' of undefined" and slider is not working
Here is the function
function FlexsliderManualDirectionControls( element, options ) {
this.element = element;
this.options = $.extend( {}, defaults, options) ;
this._flexslider = $(element).data('flexslider');
this._originalFlexsliderWrapupFunction = this._flexslider.wrapup;
this._defaults = defaults;
this._name = flexsliderManualDirectionControls;
this.init();
}
This is just a single function out of full page code.
the console says error at this._originalFlexsliderWrapupFunction = this._flexslider.wrapup;
If you find something helpful please do reply back.
Incase you need full code
/*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:false, latedef:true, newcap:true, noarg:true, noempty:true, nonew:true, undef:true, strict:false, trailing:true, browser:true, jquery:true */
/*!
* jQuery flexslider extension
* Original author: #markirby
* Licensed under the MIT license
*/
(function ( $, window, document, undefined ) {
var flexsliderManualDirectionControls = 'flexsliderManualDirectionControls',
defaults = {
previousElementSelector: ".previous",
nextElementSelector: ".next",
disabledStateClassName: "disable"
};
function FlexsliderManualDirectionControls( element, options ) {
this.element = element;
this.options = $.extend( {}, defaults, options);
this._flexslider = $(element).data('flexslider');
this._originalFlexsliderWrapupFunction = this._flexslider.wrapup;
this._defaults = defaults;
this._name = flexsliderManualDirectionControls;
this.init();
}
FlexsliderManualDirectionControls.prototype.init = function () {
this.addEventListeners();
var self = this;
this._flexslider.wrapup = function(direction) {
self.onAnimationEnd.call(self, direction);
};
};
FlexsliderManualDirectionControls.prototype.addEventListeners = function() {
$(this.element).find(this.options.previousElementSelector).bind('touchstart.flexsliderPromo click.flexsliderPromo', {self:this}, function(event) {
event.stopPropagation();
event.preventDefault();
if (!event.handled) {
event.data.self.goToTargetInDirection('prev');
event.handled = true;
}
});
$(this.element).find(this.options.nextElementSelector).bind('click.flexsliderPromo', {self:this}, function(event) {
event.stopPropagation();
event.preventDefault();
if (!event.handled) {
event.data.self.goToTargetInDirection('next');
event.handled = true;
}
});
};
FlexsliderManualDirectionControls.prototype.goToTargetInDirection = function(direction) {
var target = this._flexslider.getTarget(direction);
if (this._flexslider.canAdvance(target)) {
this._flexslider.flexAnimate(target);
}
return false;
};
FlexsliderManualDirectionControls.prototype.addOrRemoveDisabledStateForDirection = function(direction, $navElement) {
var target = this._flexslider.getTarget(direction);
if (!this._flexslider.canAdvance(target)) {
$navElement.addClass(this.options.disabledStateClassName);
} else {
$navElement.removeClass(this.options.disabledStateClassName);
}
};
FlexsliderManualDirectionControls.prototype.onAnimationEnd = function(direction) {
var $nextElement = $(this.element).find(this.options.nextElementSelector),
$previousElement = $(this.element).find(this.options.previousElementSelector);
this.addOrRemoveDisabledStateForDirection('next', $nextElement);
this.addOrRemoveDisabledStateForDirection('prev', $previousElement);
this._originalFlexsliderWrapupFunction(direction);
};
$.fn[flexsliderManualDirectionControls] = function ( options ) {
return this.each(function () {
if (!$.data(this, 'plugin_' + flexsliderManualDirectionControls)) {
$.data(this, 'plugin_' + flexsliderManualDirectionControls,
new FlexsliderManualDirectionControls( this, options ));
}
});
};
})( jQuery, window, document );

responsive top-bar navigation with mootools

i am working with mootools , and with foundation as my css "framework".
i am using the navigation top-bar from foundation and its great. yet the responsive design was ruined.
since i am not working with jquery ....
http://jsfiddle.net/idanhen/3LXQb/ <-- this is the foundation code.
http://foundation.zurb.com/docs/navigation.php <- navigation documentation
i cant understand the jquery script they did to convert it.
anyone know of a mootools responsive navigation bar ?
Made it myself , thought i would share if someone will ever need it .
original file is : "jquery.foundation.topbar.js"
new file is : "mootools.foundation.topbar.js"
just add foundation.css
mootools-core-1.4.5 , mootools-more-1.4.0.1 ( i have both cause its a huge project , but i guess u can only use the core ... )
mootools.foundation.topbar.js
and ofcourse run the following command :
<script type="text/javascript">
window.addEvent('domready', function() {
window.foundationTopBar();
});
</script>
"mootools.foundation.topbar.js" :
`
/**
* mootools.foundation.topbar
*
* taken from foundation.topbar.js
* http://foundation.zurb.com
*
* Written by Idan Hen : idandush#gmail.com
*/
;(function ($, window, undefined) {
'use strict';
/* just create settings object */
var settings = {
index : 0,
breakPoint : 940, // Set to to 9999 to force it into responsive always
initialized : false
};
var methods = {
init : function (options) {
return function () {
settings = Object.merge(settings, options); //settings = $.extend(settings, options);
settings.window = window;
settings.topbar = $$('nav.top-bar');
settings.titlebar = settings.topbar.getChildren('ul')[0]; // getElement() just return #
if (!settings.initialized) {
methods.assemble();
settings.initialized = true;
}
if (!settings.height) {
methods.largestUL();
}
$$('.top-bar .toggle-topbar').getParent().addEvent('click.fndtn:relay(.top-bar .toggle-topbar)', function (e) { //live switched to addEvent
e.preventDefault();
if (methods.breakpoint()) {
settings.topbar.toggleClass('expanded');
settings.topbar.setStyle('min-height', ''); //css
}
});
// Show the Dropdown Levels on Click
$$('.top-bar .has-dropdown>a').getParent().addEvent('click.fndtn:relay(.top-bar .has-dropdown>a)', function (e) {
e.preventDefault();
if (methods.breakpoint()) {
var anchor = $(this),
selectedLi = anchor.getParent('li'), // closest -> getParent
section = anchor.getParents('section')[0],// closest -> getParents
largestUl;
settings.index += 1;
selectedLi.addClass('moved');
section.setStyle('left', -(100 * settings.index) + '%');
section.getElements('>.name').setStyle('left', 100 * settings.index + '%');
//outerHeight
anchor.getSiblings('ul').setStyle('height', (settings.height + settings.titlebar.getSize().y));
settings.topbar.setStyle('min-height', settings.height + settings.titlebar.getSize().y * 2) //outerHeight
}
});
// Go up a level on Click
$$('.top-bar .has-dropdown .back').getParent().addEvent('click.fndtn:relay(.top-bar .has-dropdown .back)', function (e) {
e.preventDefault();
var anchor = $(this),
movedLi = anchor.getParent('li.moved'),
section = anchor.getParents('section')[0],
previousLevelUl = movedLi.getParent();
settings.index -= 1;
section.setStyle('left', -(100 * settings.index) + '%'); //css
section.getElements('>.name').setStyle('left', 100 * settings.index + '%'); // find
if (settings.index === 0) {
settings.topbar.setStyle('min-height', 0); // changed topbar from $topbar
}
setTimeout(function () {
movedLi.removeClass('moved');
}, 300);
});
}.call(window.document.HTMLDocument);
},
breakpoint : function () {
return settings.window.getSize().x < settings.breakPoint; //width()
},
assemble : function assemble() {
var section = settings.topbar.getElements('section')[0];
// Pull element out of the DOM for manipulation
section = section.dispose(); //detach
//console.log('section.getElements.n>a : ', section.getElements('.has-dropdown>a'));
section.getElements('.has-dropdown>a').each(function(e){
e.each(function (e) {
//console.log('section' , section);
var link = $(e),
dropdown = link.getSiblings('.dropdown'), //siblings
//<li class="title back js-generated"><h5></h5></li>
a = new Element('a', {
href: '#'
}),
h5 = new Element('h5', {}),
titleLi = new Element('li', {
'class': 'title back js-generated'
});//section.getChildren('ul li');// back js-generated');
// console.log('dropdown: ', dropdown);
h5.grab(a);
titleLi.grab(h5);
// Copy link to subnav
titleLi.getElements('h5>a').set('html', (link.get('html') ) ); // find -> getElements
dropdown.grab(titleLi, 'top');
});
});
// Put element back in the DOM
settings.topbar[0].grab(section[0]); // section.appendTo(settings.topbar);
},
largestUL : function () {
var uls = settings.topbar[0].getElements('section ul ul'), // find -> getElements
largest = uls.getFirst(),
total = 0;
uls.each(function(ul){
if (ul.getChildren('li').length > largest.getChildren('li').length) { //length -> getSize().x
largest = ul;
}
});
largest.getChildren('li').each(function (li) { total += li.getComputedSize().height; }); //outerHeight(true); -> getSize().y
settings.height = total;
}
};
/**
* this function is added to the window -> need to add it myself
* apply is call ...
*/
window.foundationTopBar = function (method)
{
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.foundationTopBar');
}
};
}($, this));
`

Categories

Resources