cl01/wp-content/plugins/sticky-menu-or-anything-on-scroll/assets/js/jq-sticky-anything.js
2025-02-24 12:39:24 +08:00

492 lines
17 KiB
JavaScript
Executable File

/**
* @preserve Sticky Anything 2.22 | (c) WebFactory Ltd | 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(
"cssText",
originalInlineStyles + "margin-top: " +
stickyTopMargin +
"px !important; margin-left: 0 !important"
)
.css("position", "fixed")
.css("left", leftOrgElement + "px")
.css("top", stickyTop + "px")
.css("width", widthSticky)
.css("padding", paddingSticky)
.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(
'<div class="sticky-element-placeholder" style="width:0; height:0; margin:0; padding:0; visibility:hidden;"></div>'
);
}
// 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);