/** * @preserve Sticky Anything 2.1.1 | @senff | GPL2 Licensed */ console.error('PLEASE NOTE: this is just a notification that the Sticky Menu (or Anything!) plugin is now working in Debug Mode.'); console.error('Even though this mode will not impact any functionality, do NOT use this mode in production environments!'); (function ($) { $.fn.stickThis = function(options) { var settings = $.extend({ // Default top: 0, minscreenwidth: 0, maxscreenwidth: 99999, zindex: 1, legacymode: false, dynamicmode: false, debugmode: false, pushup: '', adminbar: false }, options ); var numElements = $(this).length; var numPushElements = $(settings.pushup).length; if (numPushElements < 1) { // There are no elements on the page with the called selector for the Push-up Element. if((settings.debugmode == true) && (settings.pushup)) { console.error('STICKY ANYTHING DEBUG: There are no elements with the selector/class/ID you selected for the Push-up element ("'+settings.pushup+'").'); } // Resetting it to NOTHING. settings.pushup = ''; } else if (numPushElements > 1) { // You can't use more than one element to push up the sticky element. // Make sure that you use a selector that applies to only ONE SINGLE element on the page. // Want to find out quickly where all the elements are that you targeted? Uncomment the next line to debug. // $(settings.pushup).css('border','solid 3px #ff0000'); if(settings.debugmode == true) { console.error('STICKY ANYTHING DEBUG: There are '+numPushElements+' elements on the page with the selector/class/ID you selected for the push-up element ("'+settings.pushup+'"). You can select only ONE element to push the sticky element up.'); } // Resetting it to NOTHING. settings.pushup = ''; } if (numElements < 1) { // There are no elements on the page with the called selector. if(settings.debugmode == true) { console.error('STICKY ANYTHING DEBUG: There are no elements with the selector/class/ID you selected for the sticky element ("'+this.selector+'").'); } } else if (numElements > 1) { // This is not going to work either. You can't make more than one element sticky. They will only get in eachother's way. // Make sure that you use a selector that applies to only ONE SINGLE element on the page. // Want to find out quickly where all the elements are that you targeted? Uncomment the next line to debug. // $(this).css('border','solid 3px #00ff00'); if(settings.debugmode == true) { console.error('STICKY ANYTHING DEBUG: There There are '+numPushElements+' elements with the selector/class/ID you selected for the sticky element ("'+this.selector+'"). You can only make ONE element sticky.'); } } else { if(settings.legacymode == true) { // LEGACY MODE $(this).addClass('sticky-element-original').addClass('element-is-not-sticky'); if(settings.dynamicmode != true) { // Create a clone of the menu, right next to original (in the DOM) on initial page load createClone(settings.top,settings.zindex,settings.adminbar); } checkElement = setInterval(function(){stickItLegacy(settings.top,settings.minscreenwidth,settings.maxscreenwidth,settings.zindex,settings.pushup,settings.dynamicmode,settings.adminbar)},10); } else { // MODERN MODE $(this).addClass('sticky-element-original').addClass('element-is-not-sticky'); orgAssignedStyles = cssStyles($(this)); // All original element styles, assigned by CSS. orgInlineStyles = $('.sticky-element-original').attr('style'); if (orgInlineStyles == null) { orgInlineStyles = ''; } createPlaceholder(); checkElement = setInterval(function(){stickIt(settings.top,settings.minscreenwidth,settings.maxscreenwidth,settings.zindex,settings.pushup,settings.adminbar,orgAssignedStyles,orgInlineStyles)},10); } } return this; }; // The new StickIt function function stickIt(stickyTop,minwidth,maxwidth,stickyZindex,pushup,adminbar,originalAssignedStyles,originalInlineStyles) { // We need to check the position of the ACTIVE element. // This is the original one when it's not sticky, but when it's sticky, it's the placeholder. $listenerElement = $('.sticky-element-active'); var orgElementPos = $listenerElement.offset(); orgElementTop = orgElementPos.top; if(pushup) { var pushElementPos = $(pushup).offset(); pushElementTop = pushElementPos.top; } // Calculating actual viewport width var e = window, a = 'inner'; if (!('innerWidth' in window )) { a = 'client'; e = document.documentElement || document.body; } viewport = e[ a+'Width' ]; if ((adminbar) && $('body').hasClass('admin-bar') && (viewport > 600)) { // below 600, the adminbar is not fixed adminBarHeight = $('#wpadminbar').height(); } else { adminBarHeight = 0; } if (($(window).scrollTop() >= (orgElementTop - stickyTop - adminBarHeight)) && (viewport >= minwidth) && (viewport <= maxwidth)) { // We've scrolled PAST the original position; this is where we need to make the element sticky. // Placeholder element should always have same left position as original element (see comment below). // The sticky element will NOT have a TOP or the LEFT margin. This is because the left/top reference point of the original // element does not consider the margin. So, we're checking the left/top point of the actual original element and then // use that position for the sticky element. // LEFT POSITION coordsOrgElement = $listenerElement.offset(); leftOrgElement = coordsOrgElement.left; // This is the position REGARDLESS of the margin. // WIDTH/HEIGHT // The placeholder needs to have the width and height of the original element, WITHOUT the margins but WITH the padding and borders // Whatever margins the original has, the placeholder needs to have that too. widthPlaceholder = $listenerElement[0].getBoundingClientRect().width; if (!widthPlaceholder) { widthPlaceholder = $listenerElement.css('width'); // FALLBACK for subpixels } heightPlaceholder = $listenerElement[0].getBoundingClientRect().height; if (!heightPlaceholder) { heightPlaceholder = $listenerElement.css('height'); // FALLBACK for subpixels } // WIDTH/HEIGHT OF STICKY ELEMENT // The original element though, needs to have the inner width and height of the original (non-sticky) element // No padding, no borders, because that will be applied later anyway, regardless of box-sizing widthSticky = $('.sticky-element-original').css('width'); if(widthSticky == '0px') { widthSticky = ($('.sticky-element-original')[0].getBoundingClientRect().width); } heightSticky = $('.sticky-element-original').height(); // PADDING // If padding is percentages, convert to pixels when it becomes sticky // Just a leftover from the old method. We will not use padding for the placeholder paddingOrgElement = [$('.sticky-element-original').css('padding-top'), $('.sticky-element-original').css('padding-right'), $('.sticky-element-original').css('padding-bottom'),$('.sticky-element-original').css('padding-left')]; paddingSticky = paddingOrgElement[0] + ' ' + paddingOrgElement[1] + ' ' + paddingOrgElement[2] + ' ' + paddingOrgElement[3]; // MARGIN marginOrgElement = [$listenerElement.css('margin-top'), $listenerElement.css('margin-right'), $listenerElement.css('margin-bottom'),$listenerElement.css('margin-left')]; marginPlaceholder = marginOrgElement[0] + ' ' + marginOrgElement[1] + ' ' + marginOrgElement[2] + ' ' + marginOrgElement[3]; // OTHER ELEMENTS // if original has float, display, etc., we need to assign that to the placeholder // Though not as important as the width/height/margin/padding assignedStyles = ''; for (var importantStyle in originalAssignedStyles) { if (originalAssignedStyles[importantStyle] == 'inline') { assignedStyles += importantStyle+':inline-block; '; } else { assignedStyles += importantStyle+':'+originalAssignedStyles[importantStyle]+'; '; } } // Fixes bug where height of original element returns zero // Is this still needed for the post-2.0 mode?? elementHeight = 0; if (heightPlaceholder < 1) { elementHeight = $('.sticky-element-cloned').outerHeight(); } else { elementHeight = $('.sticky-element-original').outerHeight(); } // If scrolled position = pushup-element (top coordinate) - space between top and element - element height - admin bar // In other words, if the pushup element hits the bottom of the sticky element if (pushup && ($(window).scrollTop() > (pushElementTop-stickyTop-elementHeight-adminBarHeight))) { stickyTopMargin = (pushElementTop-stickyTop-elementHeight-$(window).scrollTop()); } else { stickyTopMargin = adminBarHeight; } assignedStyles += 'width:'+widthPlaceholder+'px; height:'+heightPlaceholder+'px; margin:'+marginPlaceholder+';'; $('.sticky-element-original').removeClass('sticky-element-active').removeClass('element-is-not-sticky').addClass('element-is-sticky').css('position','fixed').css('left',leftOrgElement+'px').css('top',stickyTop+'px').css('width',widthSticky).css('margin-left',0).css('padding',paddingSticky).css('margin-top',stickyTopMargin).css('z-index',stickyZindex); if(!$('.sticky-element-placeholder').hasClass('sticky-element-active')) { $('.sticky-element-placeholder').addClass('sticky-element-active').attr('style',assignedStyles); } } else { // not scrolled past the menu; only show the original element. $('.sticky-element-original').addClass('sticky-element-active').removeClass('element-is-sticky').addClass('element-is-not-sticky').attr('style',originalInlineStyles); if($('.sticky-element-placeholder').hasClass('sticky-element-active')) { $('.sticky-element-placeholder').removeClass('sticky-element-active').removeAttr('style').css('width','0').css('height','0').css('margin','0').css('padding','0'); } } } function createPlaceholder() { $('.sticky-element-original').addClass('sticky-element-active').before(''); } // Helper function: get the important CSS rules from an element function cssStyles(el) { o = {}; o['display'] = el.css('display'); o['float'] = el.css('float'); o['flex'] = el.css('flex'); o['box-sizing'] = el.css('box-sizing'); o['clear'] = el.css('clear'); o['overflow'] = el.css('overflow'); o['transform'] = el.css('transform'); // For some reason, this original loop doesn't work with some themes/plugins. // importantStyles = ['display','float','flex','box-sizing','clear','overflow','transform']; // for(var styleProp in importantStyles) { // o[importantStyles[styleProp]] = el.css(importantStyles[styleProp]); // } return o; } // The old StickIt function function stickItLegacy(stickyTop,minwidth,maxwidth,stickyZindex,pushup,dynamic,adminbar) { var orgElementPos = $('.sticky-element-original').offset(); orgElementTop = orgElementPos.top; if(pushup) { var pushElementPos = $(pushup).offset(); pushElementTop = pushElementPos.top; } // Calculating actual viewport width var e = window, a = 'inner'; if (!('innerWidth' in window )) { a = 'client'; e = document.documentElement || document.body; } viewport = e[ a+'Width' ]; if ((adminbar) && $('body').hasClass('admin-bar') && (viewport > 600)) { adminBarHeight = $('#wpadminbar').height(); } else { adminBarHeight = 0; } if (($(window).scrollTop() >= (orgElementTop - stickyTop - adminBarHeight)) && (viewport >= minwidth) && (viewport <= maxwidth)) { // scrolled past the original position; now only show the cloned, sticky element. // Cloned element should always have same left position and width as original element. orgElement = $('.sticky-element-original'); coordsOrgElement = orgElement.offset(); leftOrgElement = coordsOrgElement.left; widthOrgElement = orgElement[0].getBoundingClientRect().width; if (!widthOrgElement) { widthOrgElement = orgElement.css('width'); // FALLBACK for subpixels } heightOrgElement = orgElement.outerHeight(); // If padding is percentages, convert to pixels paddingOrgElement = [orgElement.css('padding-top'), orgElement.css('padding-right'), orgElement.css('padding-bottom'), orgElement.css('padding-left')]; paddingCloned = paddingOrgElement[0] + ' ' + paddingOrgElement[1] + ' ' + paddingOrgElement[2] + ' ' + paddingOrgElement[3]; if( (dynamic == true) && ($('.sticky-element-cloned').length < 1) ) { // DYNAMIC MODE: if there is no clone present, create it right now createClone(stickyTop,stickyZindex); } // Fixes bug where height of original element returns zero elementHeight = 0; if (heightOrgElement < 1) { elementHeight = $('.sticky-element-cloned').outerHeight(); } else { elementHeight = $('.sticky-element-original').outerHeight(); } // If scrolled position = pushup-element (top coordinate) - space between top and element - element height - admin bar // In other words, if the pushup element hits the bottom of the sticky element if (pushup && ($(window).scrollTop() > (pushElementTop-stickyTop-elementHeight-adminBarHeight))) { stickyTopMargin = (pushElementTop-stickyTop-elementHeight-$(window).scrollTop()); } else { stickyTopMargin = adminBarHeight; } $('.sticky-element-cloned').css('left',leftOrgElement+'px').css('top',stickyTop+'px').css('width',widthOrgElement).css('margin-top',stickyTopMargin).css('padding',paddingCloned).show(); $('.sticky-element-original').css('visibility','hidden'); } else { // not scrolled past the menu; only show the original menu. if(dynamic == true) { $('.sticky-element-cloned').remove(); } else { $('.sticky-element-cloned').hide(); } $('.sticky-element-original').css('visibility','visible'); } } function createClone(cloneTop,cloneZindex) { $('.sticky-element-original').clone().insertAfter($('.sticky-element-original')).addClass('sticky-element-cloned').removeClass('element-is-not-sticky').addClass('element-is-sticky').css('position','fixed').css('top',cloneTop+'px').css('margin-left','0').css('z-index',cloneZindex).removeClass('sticky-element-original').hide(); } }(jQuery));