Mobiles Seitenlayout für Contao 4.8+
Hallo zusamen,
wir nutzen das Modul aktuell mit Contao 4.13.39. Die mobile Navigation hat unter 4.4.57 fehlerfrei funktioniert. Mir ist heute ein Kuriosum aufgefallen, wenn man auf die Seite kommt wird das Hamburger Menü mit der Haupt- und Subnavigation angezeigt. Klickt man eine Unterseite an, wird das Hamburger Menü nicht mehr angezeigt. Dreht man das Smartphone auf Querformat, wird es angezeigt. Danach wird es auch im Hochformat angezeigt. Klickt man dann eine andere Seite an, gleicher Fehler.
Eigener JavaScript-Code im Seitenlayout:
Code:
<script src="files/theme_files/responsive_navigation/jquery.responsinav.min.js"></script>
<script>
$(function(){
$(window).responsinav({ breakpoint: 1139 });
});
</script>
<script type="text/javascript" src="files/theme_files/quicksand/jquery.quicksand.js"></script>
<script type="text/javascript" src="files/theme_files/quicksand/quicksand.js"></script>
jquery.quicksand.js
Code:
/*
Quicksand 1.3
Reorder and filter items with a nice shuffling animation.
Copyright (c) 2010 Jacek Galanciak (razorjack.net) and agilope.com
Big thanks for Piotr Petrus (riddle.pl) for deep code review and wonderful docs & demos.
Dual licensed under the MIT and GPL version 2 licenses.
http://github.com/jquery/jquery/blob/master/MIT-LICENSE.txt
http://github.com/jquery/jquery/blob/master/GPL-LICENSE.txt
Project site: http://razorjack.net/quicksand
Github site: http://github.com/razorjack/quicksand
*/
(function($) {
$.fn.quicksand = function(collection, customOptions) {
var options = {
duration : 750,
easing : 'swing',
attribute : 'data-id', // attribute to recognize same items within source and dest
adjustHeight : 'auto', // 'dynamic' animates height during shuffling (slow), 'auto' adjusts it
// before or after the animation, false leaves height constant
adjustWidth : 'auto', // 'dynamic' animates width during shuffling (slow),
// 'auto' adjusts it before or after the animation, false leaves width constant
useScaling : false, // enable it if you're using scaling effect
enhancement : function(c) {}, // Visual enhacement (eg. font replacement) function for cloned elements
selector : '> *',
atomic : false,
dx : 0,
dy : 0,
maxWidth : 0,
retainExisting : true // disable if you want the collection of items to be replaced completely by incoming items.
};
$.extend(options, customOptions);
// Got IE and want scaling effect? Kiss my ass.
if (navigator.userAgent.match(/msie/i) || (typeof ($.fn.scale) == 'undefined')) {
options.useScaling = false;
}
var callbackFunction;
if (typeof (arguments[1]) == 'function') {
callbackFunction = arguments[1];
} else if (typeof (arguments[2] == 'function')) {
callbackFunction = arguments[2];
}
return this.each(function(i) {
var val;
var animationQueue = []; // used to store all the animation params before starting the animation;
// solves initial animation slowdowns
var $collection;
if (typeof(options.attribute) == 'function') {
$collection = $(collection);
} else {
$collection = $(collection).filter('[' + options.attribute + ']').clone(); // destination (target) collection
}
var $sourceParent = $(this); // source, the visible container of source collection
var sourceHeight = $(this).css('height'); // used to keep height and document flow during the animation
var sourceWidth = $(this).css('width'); // used to keep width and document flow during the animation
var destHeight, destWidth;
var adjustHeightOnCallback = false;
var adjustWidthOnCallback = false;
var offset = $($sourceParent).offset(); // offset of visible container, used in animation calculations
var offsets = []; // coordinates of every source collection item
var $source = $(this).find(options.selector); // source collection items
var width = $($source).innerWidth(); // need for the responsive design
// Replace the collection and quit if IE6
if (navigator.userAgent.match(/msie/i) && navigator.userAgent.match(/6/)) {
$sourceParent.html('').append($collection);
return;
}
// Gets called when any animation is finished
var postCallbackPerformed = 0; // prevents the function from being called more than one time
var postCallback = function() {
$(this).css('margin', '').css('position', '').css('top', '').css('left', '').css('opacity', '');
if (!postCallbackPerformed) {
postCallbackPerformed = 1;
if (!options.atomic) {
// hack: used to be: $sourceParent.html($dest.html());
// put target HTML into visible source container
// but new webkit builds cause flickering when replacing the collections
var $toDelete = $sourceParent.find(options.selector);
if (!options.retainExisting) {
$sourceParent.prepend($dest.find(options.selector));
$toDelete.remove();
} else {
// Avoid replacing elements because we may have already altered items in significant
// ways and it would be bad to have to do it again. (i.e. lazy load images)
// But $dest holds the correct ordering. So we must re-sequence items in $sourceParent to match.
var $keepElements = $([]);
$dest.find(options.selector).each(function(i) {
var $matchedElement = $([]);
if (typeof (options.attribute) == 'function') {
var val = options.attribute($(this));
$toDelete.each(function() {
if (options.attribute(this) == val) {
$matchedElement = $(this);
return false;
}
});
} else {
$matchedElement = $toDelete.filter(
'[' + options.attribute + '="'+
$(this).attr(options.attribute) + '"]');
}
if ($matchedElement.length > 0) {
// There is a matching element in the $toDelete list and in $dest
// list, so make sure it is in the right location within $sourceParent
// and put it in the list of elements we need to not delete.
$keepElements = $keepElements.add($matchedElement);
if (i === 0) {
$sourceParent.prepend($matchedElement);
} else {
$matchedElement.insertAfter($sourceParent.find(options.selector).get(i - 1));
}
}
});
// Remove whatever is remaining from the DOM
$toDelete.not($keepElements).remove();
}
if (adjustHeightOnCallback) {
$sourceParent.css('height', destHeight);
}
if (adjustWidthOnCallback) {
$sourceParent.css('width', sourceWidth);
}
}
options.enhancement($sourceParent); // Perform custom visual enhancements on a newly replaced collection
if (typeof callbackFunction == 'function') {
callbackFunction.call(this);
}
}
if (false === options.adjustHeight) {
$sourceParent.css('height', 'auto');
}
if (false === options.adjustWidth) {
$sourceParent.css('width', 'auto');
}
};
// Position: relative situations
var $correctionParent = $sourceParent.offsetParent();
var correctionOffset = $correctionParent.offset();
if ($correctionParent.css('position') == 'relative') {
if ($correctionParent.get(0).nodeName.toLowerCase() != 'body') {
correctionOffset.top += (parseFloat($correctionParent.css('border-top-width')) || 0);
correctionOffset.left += (parseFloat($correctionParent.css('border-left-width')) || 0);
}
} else {
correctionOffset.top -= (parseFloat($correctionParent.css('border-top-width')) || 0);
correctionOffset.left -= (parseFloat($correctionParent.css('border-left-width')) || 0);
correctionOffset.top -= (parseFloat($correctionParent.css('margin-top')) || 0);
correctionOffset.left -= (parseFloat($correctionParent.css('margin-left')) || 0);
}
// perform custom corrections from options (use when Quicksand fails to detect proper correction)
if (isNaN(correctionOffset.left)) {
correctionOffset.left = 0;
}
if (isNaN(correctionOffset.top)) {
correctionOffset.top = 0;
}
correctionOffset.left -= options.dx;
correctionOffset.top -= options.dy;
// keeps nodes after source container, holding their position
$sourceParent.css('height', $(this).height());
$sourceParent.css('width', $(this).width());
// get positions of source collections
$source.each(function(i) {
offsets[i] = $(this).offset();
});
// stops previous animations on source container
$(this).stop();
var dx = 0;
var dy = 0;
$source.each(function(i) {
$(this).stop(); // stop animation of collection items
var rawObj = $(this).get(0);
if (rawObj.style.position == 'absolute') {
dx = -options.dx;
dy = -options.dy;
} else {
dx = options.dx;
dy = options.dy;
}
rawObj.style.position = 'absolute';
rawObj.style.margin = '0';
if (!options.adjustWidth) {
rawObj.style.width = (width + 'px'); // sets the width to the current element
// with even if it has been changed
// by a responsive design
}
rawObj.style.top = (offsets[i].top- parseFloat(rawObj.style.marginTop) - correctionOffset.top + dy) + 'px';
rawObj.style.left = (offsets[i].left- parseFloat(rawObj.style.marginLeft) - correctionOffset.left + dx) + 'px';
if (options.maxWidth > 0 && offsets[i].left > options.maxWidth) {
rawObj.style.display = 'none';
}
});
// create temporary container with destination collection
var $dest = $($sourceParent).clone();
var rawDest = $dest.get(0);
rawDest.innerHTML = '';
rawDest.setAttribute('id', '');
rawDest.style.height = 'auto';
rawDest.style.width = $sourceParent.width() + 'px';
$dest.append($collection);
// Inserts node into HTML. Note that the node is under visible source container in the exactly same position
// The browser render all the items without showing them (opacity: 0.0) No offset calculations are needed,
// the browser just extracts position from underlayered destination items and sets animation to destination positions.
$dest.insertBefore($sourceParent);
$dest.css('opacity', 0.0);
rawDest.style.zIndex = -1;
rawDest.style.margin = '0';
rawDest.style.position = 'absolute';
rawDest.style.top = offset.top - correctionOffset.top + 'px';
rawDest.style.left = offset.left - correctionOffset.left + 'px';
if (options.adjustHeight === 'dynamic') {
// If destination container has different height than source container the height can be animated,
// adjusting it to destination height
$sourceParent.animate({ height : $dest.height() }, options.duration, options.easing);
} else if (options.adjustHeight === 'auto') {
destHeight = $dest.height();
if (parseFloat(sourceHeight) < parseFloat(destHeight)) {
// Adjust the height now so that the items don't move out of the container
$sourceParent.css('height', destHeight);
} else {
// Adjust later, on callback
adjustHeightOnCallback = true;
}
}
if (options.adjustWidth === 'dynamic') {
// If destination container has different width than source container the width can be animated,
// adjusting it to destination width
$sourceParent.animate({ width : $dest.width() }, options.duration, options.easing);
} else if (options.adjustWidth === 'auto') {
destWidth = $dest.width();
if (parseFloat(sourceWidth) < parseFloat(destWidth)) {
// Adjust the height now so that the items don't move out of the container
$sourceParent.css('width', destWidth);
} else {
// Adjust later, on callback
adjustWidthOnCallback = true;
}
}
// Now it's time to do shuffling animation. First of all, we need to identify same elements within
// source and destination collections
$source.each(function(i) {
var destElement = [];
if (typeof (options.attribute) == 'function') {
val = options.attribute($(this));
$collection.each(function() {
if (options.attribute(this) == val) {
destElement = $(this);
return false;
}
});
} else {
destElement = $collection.filter('[' + options.attribute + '="' + $(this).attr(options.attribute) + '"]');
}
if (destElement.length) {
// The item is both in source and destination collections. It it's under different position, let's move it
if (!options.useScaling) {
animationQueue.push({
element : $(this), dest : destElement,
style : {
top : $(this).offset().top,
left : $(this).offset().left,
opacity : ""
},
animation : {
top : destElement.offset().top - correctionOffset.top,
left : destElement.offset().left - correctionOffset.left,
opacity : 1.0
}
});
} else {
animationQueue.push({
element : $(this), dest : destElement,
style : {
top : $(this).offset().top,
left : $(this).offset().left,
opacity : ""
},
animation : {
top : destElement.offset().top - correctionOffset.top,
left : destElement.offset().left - correctionOffset.left,
opacity : 1.0,
scale : '1.0'
}
});
}
} else {
// The item from source collection is not present in destination collections. Let's remove it
if (!options.useScaling) {
animationQueue.push({
element : $(this),
style : {
top : $(this).offset().top,
left : $(this).offset().left,
opacity : ""
},
animation : {
opacity : '0.0'
}
});
} else {
animationQueue.push({
element : $(this),
animation : {
opacity : '0.0',
style : {
top : $(this).offset().top,
left : $(this).offset().left,
opacity : ""
},
scale : '0.0'
}
});
}
}
});
$collection.each(function(i) {
// Grab all items from target collection not present in visible source collection
var sourceElement = [];
var destElement = [];
if (typeof (options.attribute) == 'function') {
val = options.attribute($(this));
$source.each(function() {
if (options.attribute(this) == val) {
sourceElement = $(this);
return false;
}
});
$collection.each(function() {
if (options.attribute(this) == val) {
destElement = $(this);
return false;
}
});
} else {
sourceElement = $source.filter('[' + options.attribute + '="' + $(this).attr(options.attribute) + '"]');
destElement = $collection.filter('[' + options.attribute + '="' + $(this).attr(options.attribute) + '"]');
}
var animationOptions;
if (sourceElement.length === 0 && destElement.length > 0) {
// No such element in source collection...
if (!options.useScaling) {
animationOptions = {opacity : '1.0'};
} else {
animationOptions = {opacity : '1.0', scale : '1.0'};
}
// Let's create it
var d = destElement.clone();
var rawDestElement = d.get(0);
rawDestElement.style.position = 'absolute';
rawDestElement.style.margin = '0';
if (!options.adjustWidth) {
// sets the width to the current element with even if it has been changed by a responsive design
rawDestElement.style.width = width + 'px';
}
rawDestElement.style.top = destElement.offset().top - correctionOffset.top + 'px';
rawDestElement.style.left = destElement.offset().left - correctionOffset.left + 'px';
d.css('opacity', 0.0); // IE
if (options.useScaling) {
d.css('transform', 'scale(0.0)');
}
d.appendTo($sourceParent);
if (options.maxWidth === 0 || destElement.offset().left < options.maxWidth) {
animationQueue.push({element : $(d), dest : destElement,animation : animationOptions});
}
}
});
$dest.remove();
if (!options.atomic) {
options.enhancement($sourceParent); // Perform custom visual enhancements during the animation
for (i = 0; i < animationQueue.length; i++) {
animationQueue[i].element.animate(animationQueue[i].animation, options.duration, options.easing, postCallback);
}
} else {
$toDelete = $sourceParent.find(options.selector);
$sourceParent.prepend($dest.find(options.selector));
for (i = 0; i < animationQueue.length; i++) {
if (animationQueue[i].dest && animationQueue[i].style) {
var destElement = animationQueue[i].dest;
var destOffset = destElement.offset();
destElement.css({
position : 'relative',
top : (animationQueue[i].style.top - destOffset.top),
left : (animationQueue[i].style.left - destOffset.left)
});
destElement.animate({top : "0", left : "0"},
options.duration,
options.easing,
postCallback);
} else {
animationQueue[i].element.animate(animationQueue[i].animation,
options.duration,
options.easing,
postCallback);
}
}
$toDelete.remove();
}
});
};
})(jQuery);
quicksand.js
Code:
$(document).ready(function(){
var items = $('#container .mod_newslist li'),
itemsByTags = {};
// Looping though all the li items:
items.each(function(i){
var elem = $(this),
tags = elem.data('tags').split(',');
// Adding a data-id attribute. Required by the Quicksand plugin:
elem.attr('data-id',i);
$.each(tags,function(key,value){
// Removing extra whitespace:
value = $.trim(value);
if(!(value in itemsByTags)){
// Create an empty array to hold this item:
itemsByTags[value] = [];
}
// Each item is added to one array per tag:
itemsByTags[value].push(elem);
});
});
// Creating the "Everything" option in the menu:
createList('Everything',items);
// Looping though the arrays in itemsByTags:
$.each(itemsByTags,function(k,v){
createList(k,v);
});
$('#portfolio_filter').on('click', 'a', function(e){
var link = $(this);
link.addClass('active').siblings().removeClass('active');
// Using the Quicksand plugin to animate the li items.
// It uses data('list') defined by our createList function:
$('#container .mod_newslist').quicksand(link.data('list').find('li'));
e.preventDefault();
});
$('#portfolio_filter a:first').click();
function createList(text,items){
// This is a helper function that takes the
// text of a menu button and array of li items
// Creating an empty unordered list:
var ul = $('<ul>',{'class':'hidden'});
$.each(items,function(){
// Creating a copy of each li item
// and adding it to the list:
$(this).clone().appendTo(ul);
});
ul.appendTo('#container');
// Creating a menu item. The unordered list is added
// as a data parameter (available via .data('list'):
var a = $('<a>',{
html: text,
href:'#',
data: {list:ul}
}).appendTo('#portfolio_filter');
}
});
Jemand eine Idee, woran das liegen könnte?