Basic
<script> document.addEventListener('DOMContentLoaded', function() { }); </script>
Basic jQuery
<script> document.addEventListener('DOMContentLoaded', function() { jQuery(function($){ }); }); </script>
for screenshots of advanced panel Elementor tutorial
.elementor-panel-navigation { position: sticky; top: 0; z-index: 99; background: #fff; }
Transition
transition: transform 0.7s cubic-bezier(1, 0, 0, 1);
Media Query Desktop Only
@media (min-width:1025px){
Media Query Tablet and Mobile
@media (max-width:1024px){
Media Query Desktop and Tablet
@media (min-width:768px){
Media Query Mobile Only
@media (max-width:767px){
Media Query Tablet Only
@media (min-width:768px) and (max-width:1024px){
Media Query Hover Hover and Hover None
let noHover = window.matchMedia('(hover:none)').matches; let hasHover = !noHover; in CSS: @media (hover: none) { }
Media Query Change Listener
let isMobile = window.matchMedia('(max-width: 767px)'); /* isMobile.matches returns true or false */ isMobile.onchange = (e) => { location.reload(); }
Equivalent of Media Query Change Listener, better browser support
let isMobile = window.innerWidth < 768; window.addEventListener('resize', function () { if (window.innerWidth < 768 && !isMobile) { location.reload(); } if (window.innerWidth >= 768 && isMobile) { location.reload(); } });
Remove WordPress admin bar
div#wpadminbar { display: none; } body {margin-top: -32px!important;}
Editor JS
/* Trigger dummy window load event if in Elementor Editor. This if for live debugging Note: Updates on each keypress. */ if(window.elementorProPreview !== undefined){ window.dispatchEvent(new CustomEvent('load')); }
inline background SVG clean code
selector .elementor-button::after{ display: inline-block; content:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' preserveAspectRatio='none' viewBox='0 0 50 50'><path d='M12.07,0a3.45,3.45,0,0,0,0,6.9H38.23L1,44.11A3.45,3.45,0,0,0,5.89,49L43.1,11.77V37.93a3.45,3.45,0,0,0,6.9,0V0Z'></path></svg>"); width: 16px; transition: all .8s; margin-left: 4px; } notes: # urlencodes to %23 avoid " in the svg, use '
referrer if log in from super socializer
document.referrer https://element.how/?SuperSocializerAuth
Make a column square (or other selected element) from Frank
add this in the column and it will become a square selector:before { content:""; padding-bottom: 100%; } Usually I make a few classes. so that I don't have to repeat the CSS .sq::before { content: ""; padding-bottom: 100%; } When I need a square column, I simply give it the classname sq You will see other columns in the same row will become square aswell. This is caused by the flex behavior. You can override this with the align-self property so that column takes only the space it needs. other classes I make: .ratio16-9::before { content: ""; padding-bottom: 56.25%; } .ratio5-4::before { content: ""; padding-bottom: 80%; } .ratio4-3::before { content: ""; padding-bottom: 75%; }
Design Ideas Websites
Landing pages: https://www.btw.so/marketing/landing-page-examples
Remove duplicate elements from the array using a new Set and spread function
let categoriesArray = []; for (let i = 0; i < list.length; i++) { categoriesArray.push(list[i].category); } categoriesArray = [...new Set(categoriesArray)];
Animation
animation: 0.9s forwards slideUp cubic-bezier(.56,.17,.68,1) 1.9s; @keyframes slideUp { 0% { transform: translateY(0%); } 10% { transform: translateY(1.8%); } 100% { transform: translateY(-110%); } }
proper way to refresh page
Always refresh your page with CTRL/CMD + SHIFT + R, or CTRL/CMD + click reload icon (Not just CTRL/CMD + R, or click the reload icon) That way it forces your browser to get all the files fresh from the server, instead of using the local copy it may have.
Dynamically / Conditionally toggle element display in Elementor with CSS only and HAS selector
/* excludes when in the editor, here it won't show the element on the front end if there is no element within with the classes "elementor-grid products" */ body:not(.elementor-editor-active) selector:not(:has(.elementor-grid.products)) { display: none; }
Preload the images after the first scroll event on the page
/* preload the images after the first image gets in the viewport */ (function () { let images = document.querySelectorAll('.switchScrollContainer .elementor-image-gallery img'); let firstImage = images[0]; let options = { root: null, rootMargin: '0px', threshold: 0.3 }; let observer = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { images.forEach(img => { addPreloadLink(img); }); observer.unobserve(firstImage); } }); }, options); observer.observe(firstImage); })(); function addPreloadLink(img) { let link = document.createElement('link'); link.rel = 'preload'; link.href = img.src || img.dataset.src; link.as = 'image'; if (img.srcset) link.setAttribute('imagesrcset', img.srcset); if (img.sizes) link.setAttribute('imagesizes', img.sizes); document.head.appendChild(link); } /* END OF preload the images after the first image gets in the viewport */
Animation
.animation-section .elementor-column:hover .elementor-image { animation: bobbing 0.88s ease-in-out 0s infinite alternate; } @keyframes bobbing { 0% { transform: translateY(0px); } 100% { transform: translateY(12px); } }
Add Elementor template from JavaScript & PHP
document.addEventListener('DOMContentLoaded', function () { let pageContent = document.querySelector('.elementor.elementor-63'); if (pageContent.innerHTML.length < 300) { pageContent.innerHTML = `<?php ob_start(); echo do_shortcode( '' ); echo ob_get_clean(); ?>`; window.addEventListener('load', function () { document.getElementById("go-back").addEventListener("click", () => { history.back(); }); }); } });
Good looking add or remove quantity input, with a word in it like "4 computers"
<form class="counter-with-text-container"> <input type="text" class="counterCalcWithText" id="displayCount" value="0 display" data-text="display"> <button class="removeOne">–</button> <button class="addOne">+</button> </form> <style> .counter-with-text-container { display: flex; align-items: center; background: #fff; border-radius: 999px; padding: 5px; justify-content: space-between; border: 1px solid #333; } .counterCalcWithText.counterCalcWithText.counterCalcWithText { flex-grow: 1; border: none; border-radius: 10px; padding: 10px 20px; margin: 0 10px; color: #333; user-select: none; color: #5B6971; font-size: 1rem; font-style: normal; font-weight: 450; letter-spacing: 0.56px; text-transform: uppercase; } .removeOne.removeOne, .addOne.addOne { border: none; background-color: transparent; color: #007BFF; font-size: 1.6rem; cursor: pointer; outline: none; font-weight: 900; } </style> <script> document.addEventListener('DOMContentLoaded', () => { const counterWithTextsContainers = document.querySelectorAll('.counter-with-text-container'); counterWithTextsContainers.forEach(counterContainer => { const valueInput = counterContainer.querySelector('.counterCalcWithText'); const removeOne = counterContainer.querySelector('.removeOne'); const addOne = counterContainer.querySelector('.addOne'); const text = valueInput.getAttribute('data-text'); removeOne.addEventListener('click', (event) => { event.preventDefault(); changeCount(-1); }); addOne.addEventListener('click', (event) => { event.preventDefault(); changeCount(1); }); function changeCount(amount) { let currentValue = parseInt(valueInput.value) || 0; currentValue += amount; if (currentValue < 0) currentValue = 0; // Prevents the count from going below 0 valueInput.value = `${currentValue} ${text}${currentValue !== 1 ? 'S' : ''}`; } }); }); </script>
Good looking add or remove quantity input, starts with just the word "Add" for clean UI.
<form class="counter-add-container"> <button class="removeOne">–</button> <input type="text" class="counterCalcAdd counterCalcAddScreen" value="0"> <button class="addOne">+</button> </form> <style> .counter-add-container { display: flex; align-items: center; background: #fff; border-radius: 999px; justify-content: space-between; position: relative; max-width: 110px; margin-left:auto; border: 1px solid transparent; /* has to be transparent, change it below instead */ } .counter-add-container:not(.isActive)::before { content:'Add'; position: absolute; width: 100%; height: 100%; border: 1px solid #333; left: 0; top: 0; display: flex; align-items: center; justify-content: center; background-color: #fff; z-index: 4; border-radius: 999px; pointer-events: none; } .counter-add-container.isActive { border-color: #333; } .counterCalcAdd.counterCalcAdd.counterCalcAdd { flex-grow: 1; border: none; border-radius: 10px; padding:4px; color: #333; user-select: none; color: #5B6971; font-size: 1rem; font-style: normal; font-weight: 450; letter-spacing: 0.56px; text-transform: uppercase; } .removeOne.removeOne, .addOne.addOne { border: none; background-color: transparent; color: #007BFF; font-size: 1.4rem; cursor: pointer; outline: none; font-weight: 900; } .counter-add-container:not(.isActive) .addOne { position: absolute; width: 100%; height: 100%; left: 0; top: 0; z-index: 3; } </style> <script> document.addEventListener('DOMContentLoaded', () => { const counterContainers = document.querySelectorAll('.counter-add-container'); counterContainers.forEach(counterContainer => { const input = counterContainer.querySelector('.counterCalcAdd'); const removeOne = counterContainer.querySelector('.removeOne'); const addOne = counterContainer.querySelector('.addOne'); removeOne.addEventListener('click', (event) => { event.preventDefault(); // Prevent the default action changeCount(-1); }); addOne.addEventListener('click', (event) => { event.preventDefault(); // Prevent the default action changeCount(1); }); function changeCount(amount) { let currentValue = parseInt(input.value) || 0; currentValue += amount; if (currentValue < 0) currentValue = 0; // Prevents the count from going below 0 (currentValue == 0)? counterContainer.classList.remove('isActive') : counterContainer.classList.add('isActive'); input.value = currentValue; } }); }); </script>
PHP prevent PHP code from running in Elementor editor
if (\Elementor\Plugin::$instance->editor->is_edit_mode() || \Elementor\Plugin::$instance->preview->is_preview_mode() ) { return; }
Elementor Form with multi step, scroll back up to the top of the form on each step click
window.addEventListener('load', () => { setTimeout(() => { const buttons = document.querySelectorAll('.e-form__buttons__wrapper__button'); buttons.forEach(button => { button.addEventListener('click', () => { const form = button.closest('.elementor-widget-form'); if (form) { const formTop = form.getBoundingClientRect().top + window.scrollY - 100; jQuery('html, body').animate({ scrollTop: formTop }, 900); } }); }); }, 900); });
Create a new swiper from a existing Elementor swiper with passparams
window.addEventListener('load', function () { setTimeout(function () { let swiperCont = document.querySelector('.centeredCarousel .swiper-container, .centeredCarousel .swiper'); let swiperInstance = swiperCont.swiper; let swiperParams = swiperCont.swiper.passedParams; swiperInstance.destroy(); new Swiper(swiperCont, { ...swiperParams, pagination: { type: 'fraction', el: '.elementor-heading-title .centeredCarouselPagination', }, handleElementorBreakpoints: false, on: { /* empty, to undo the elementor defaults */ }, centeredSlides: true, slidesPerView: 1.1, spaceBetween: 10, breakpoints: { /* when window width is >= 640px */ 640: { slidesPerView: 1.4, spaceBetween: 30 } } }) }, 600); });
Swiper.JS for Elementor ideal setup all ready to create a logo carousel or a category / tag images carousel
<script> window.addEventListener('load', function () { let logoSwiper; let logoCarousel = document.querySelector('.logoCarousel'); let swiperContainer = logoCarousel.querySelector('.elementor-shortcode'); let swiperWrapper = logoCarousel.querySelector('.brand-thumbnails'); let swiperSlides = logoCarousel.querySelectorAll('.brand-thumbnails > li'); swiperContainer.classList.add('swiper'); swiperContainer.classList.add('swiper-container'); swiperWrapper.classList.add('swiper-wrapper'); swiperSlides.forEach(e => e.classList.add('swiper-slide')); let swiperConfigObject = { slidesPerView: 'auto', spaceBetween: 20, loop: true, speed:3700, loopedSlidesLimit: false, loopedSlides: 16, autoplay: { delay: 0, disableOnInteraction:false, enabled:true, }, } let checkElementorFrontendInterval = setInterval(function() { if (typeof elementorFrontend !== 'undefined') { clearInterval(checkElementorFrontendInterval); if ('undefined' === typeof Swiper) { const onDemandSwiper = elementorFrontend.utils.swiper; new onDemandSwiper(swiperContainer, swiperConfigObject).then((newSwiperInstance) => { logoSwiper = newSwiperInstance; startAutoplayOnSafari() }); } else { logoSwiper = new Swiper(swiperContainer, swiperConfigObject); startAutoplayOnSafari() } } }, 100); function startAutoplayOnSafari(){ setTimeout(function(){ logoSwiper.autoplay.run(); },300) } }); </script>
scrollbar scroll bar styling
::-webkit-scrollbar-track { background-color: #F4f4f4; } ::-webkit-scrollbar { width: 10px; background-color: #F4f4f4; } ::-webkit-scrollbar-thumb { background-color: #00000044; } /* removed */ ::-webkit-scrollbar { width: 0px; } /*darker*/ ::-webkit-scrollbar-track { background-color: #b6b6b6; } ::-webkit-scrollbar { width: 14px; background-color: #F4f4f4; } ::-webkit-scrollbar-thumb { background-color: #0000008f; } firefox .divclass{ scrollbar-color: #444 #999; (thumb , track) scroll-bar-width: thin/auto/none; }
SEO
buyer intent keywords: buy, get, where to buy, suppliers, discount, coupon, deal, best, online, review, promo code, sale, near me
Media query to get devices with no hover
/* check if device has hover capabilities (ie is a mobile), checking the max width media query is required also because Samsung devices wrongly return they are (hover:hover) devices, more info: https://www.ctrl.blog/entry/css-media-hover-samsung.html */ let noHover = window.matchMedia('(hover:none)').matches || window.matchMedia('(max-width:1024px)').matches; let hasHover = !noHover; let devToolMQ = window.matchMedia('(hover:none)'); devToolMQ.onchange = () => { /* reload page for people looking in the devtool mobile preview mode */ window.location = window.location.href; }
URL structure products/%product_category%/%product_title%/ , along with a dynamic shortcode for the bread crumbs
<?php function register_products_post_type() { $args = array( 'public' => true, 'label' => 'Products', 'rewrite' => array('slug' => 'products/%product_category%'), 'has_archive' => true, 'supports' => array('title', 'thumbnail'), 'menu_icon' => 'dashicons-products', 'menu_position' => 4, ); register_post_type('products', $args); } add_action('init', 'register_products_post_type'); function create_products_taxonomy() { $args = array( 'label' => 'Product Categories', 'rewrite' => array('slug' => 'products'), 'hierarchical' => true, ); register_taxonomy('product_category', 'products', $args); } add_action('init', 'create_products_taxonomy'); function products_permalink_structure($post_link, $post) { if (false !== strpos($post_link, '%product_category%')) { $product_term = get_the_terms($post->ID, 'product_category'); if (!empty($product_term)) { $post_link = str_replace('%product_category%', array_pop($product_term)->slug, $post_link); } } return $post_link; } add_filter('post_type_link', 'products_permalink_structure', 10, 2); function custom_rewrite_rules() { add_rewrite_rule( '^products/([^/]+)/page/([0-9]{1,})/?$', 'index.php?product_category=$matches[1]&paged=$matches[2]', 'top' ); } add_action('init', 'custom_rewrite_rules'); function dynamic_breadcrumbs() { // Get the current post data $post = get_queried_object(); // Start the breadcrumb with a link to home $breadcrumb = '<nav aria-label="breadcrumbs" class="products-breadcrumb">'; $breadcrumb .= '<p><a href="' . home_url() . '">Home</a><span class="separator"> \ </span>'; // Add the Products archive link $products_page_url = home_url('/products/'); $breadcrumb .= '<a href="' . $products_page_url . '">Products</a><span class="separator">\ </span>'; // Check if the current object is a product and get the product category term if (is_singular('products')) { $terms = wp_get_post_terms($post->ID, 'product_category'); if ($terms && !is_wp_error($terms)) { // Get the first term $first_term = array_shift($terms); $breadcrumb .= '<a href="' . esc_url(get_term_link($first_term)) . '">' . esc_html($first_term->name) . '</a><span class="separator"> \ </span>'; } } // Add the current product title if (is_singular('products')) { $breadcrumb .= '<span class="last">' . get_the_title() . '</span>'; } // Close the breadcrumb $breadcrumb .= '</p></nav>'; // Return the breadcrumb HTML return $breadcrumb; } // Add a shortcode for the breadcrumbs function register_breadcrumbs_shortcode() { add_shortcode('dynamic_breadcrumbs', 'dynamic_breadcrumbs'); } add_action('init', 'register_breadcrumbs_shortcode');
Menu item pseudo element info box
/* Updated menu item pseudo element*/ li.templatesitem::before { content: 'Updated'; position: absolute; background: grey; padding: 4px 6px; font-size: 14px; border-radius: 8px; z-index: 99; left: -37px; top: -10px; color: white; }
Black & White to color image on hover
selector img { transition: all 0.3s;; filter: grayscale(100%); } selector img:hover { filter: grayscale(0%); transform: scale(1.1); /* Erase to remove the grow animation */ }
Elementor Custom Queries With A working method to return no items
function track_module_views() { // Check if a single post is being displayed, the user is logged in, and we're in the 'modules' post type if (is_single() && is_user_logged_in()) { $post_id = get_the_ID(); $queried_object = get_queried_object(); if ($queried_object && 'modules' === $queried_object->post_type) { $user_id = get_current_user_id(); $views = (array) get_user_meta($user_id, 'recently_viewed_modules', true); // If the current post is already first, no need to update if(isset($views[0]) && $post_id === $views[0]) { return; } // Remove current post ID if it already exists in the array if (($key = array_search($post_id, $views)) !== false) { unset($views[$key]); } // Prepend the current post ID and keep only the latest 3 views $views = array_slice(array_merge([$post_id], $views), 0, 3); // Save the updated views update_user_meta($user_id, 'recently_viewed_modules', $views); } } } add_action('template_redirect', 'track_module_views'); /** * Update the Elementor posts widget or portfolio widget query to show the 3 most recently viewed modules for the current user. * * @since 1.0.0 * @param \WP_Query $query The WordPress query instance. * @param \Elementor\Widget_Base $widget The widget instance. */ function my_custom_recently_viewed_modules_query( $query, $widget ) { // Check if the user is logged in if ( is_user_logged_in() ) { $user_id = get_current_user_id(); // Get the recent views from the user meta $viewed_modules = get_user_meta($user_id, 'recently_viewed_modules', true); if ( !empty($viewed_modules) ) { // Ensure we have an array $viewed_modules = (array) $viewed_modules; // Set the query to get the specific modules $query->set( 'post_type', 'modules' ); $query->set( 'post__in', $viewed_modules ); $query->set( 'orderby', 'post__in' ); $query->set( 'posts_per_page', 2 ); } else { // There's no next post, so modify the query to return nothing $query->set( 'date_query', array( array( 'after' => '2070-01-01', 'inclusive' => false ), )); $query->set( 'posts_per_page', 0 ); } } } add_action( 'elementor/query/recently_viewed_modules', 'my_custom_recently_viewed_modules_query', 10, 2 ); /** * Update the Elementor posts widget or portfolio widget query to show the 3 most recently viewed modules for the current user. * * @since 1.0.0 * @param \WP_Query $query The WordPress query instance. * @param \Elementor\Widget_Base $widget The widget instance. */ function my_custom_recently_viewed_modules_query_show_1( $query, $widget ) { // Check if the user is logged in if ( is_user_logged_in() ) { $user_id = get_current_user_id(); // Get the recent views from the user meta $viewed_modules = get_user_meta($user_id, 'recently_viewed_modules', true); if ( !empty($viewed_modules) ) { // Ensure we have an array $viewed_modules = (array) $viewed_modules; // Set the query to get the specific modules $query->set( 'post_type', 'modules' ); $query->set( 'post__in', $viewed_modules ); $query->set( 'orderby', 'post__in' ); $query->set( 'posts_per_page', 1 ); } else { // There's no next post, so modify the query to return nothing $query->set( 'date_query', array( array( 'after' => '2070-01-01', 'inclusive' => false ), )); $query->set( 'posts_per_page', 0 ); } } } add_action( 'elementor/query/recently_viewed_modules_show_one', 'my_custom_recently_viewed_modules_query_show_1', 10, 2 ); /** * Get the post that comes directly after the current one within the same post type. * * @param \WP_Query $query The WordPress query instance. */ function custom_upcoming_post( $query ) { global $post; // Get the current post ID and post date $current_post_id = $post->ID; $current_post_date = $post->post_date; // Get the next post $next_post = get_adjacent_post( false, '', false ); if ( !empty( $next_post ) ) { // There's a next post, so modify the query to return only the next post $query->set( 'post__in', array( $next_post->ID ) ); $query->set( 'orderby', 'post__in' ); } else { // There's no next post, so modify the query to return nothing $query->set( 'date_query', array( array( 'after' => $current_post_date, 'inclusive' => false ), )); $query->set( 'posts_per_page', 0 ); } } add_action( 'elementor/query/upcoming_post', 'custom_upcoming_post' ); /* delete all meta data recently_viewed_modules on the current user when visiting /wp-admin/?clear-recently_viewed_modules */ function clear_recently_viewed_modules() { // Check if the 'clear-recently_viewed_modules' query exists if (isset($_GET['clear-recently_viewed_modules'])) { // Check if the user is logged in if (is_user_logged_in()) { // Get the current user's ID $user_id = get_current_user_id(); // Delete the meta data for the current user delete_user_meta($user_id, 'recently_viewed_modules'); // Optional: Redirect to a confirmation page or the homepage wp_redirect(home_url()); exit; } } } add_action('admin_init', 'clear_recently_viewed_modules');
Pseudo Element Background Overlay After
.elementor-background-overlay::after { content: ''; position: absolute; left: 0; right: 0; top: 0; bottom: 0; background-color: #0D0E15; pointer-events: none; opacity: 0.5; }
Clip path animation for a circle progress fill
@keyframes prixClipFix { /* from https://cssloaders.github.io/ */ 0% {clip-path:polygon(50% 50%,0 0,0 0,0 0,0 0,0 0)} 50% {clip-path:polygon(50% 50%,0 0,100% 0,100% 0,100% 0,100% 0)} 75%, 100% {clip-path:polygon(50% 50%,0 0,100% 0,100% 100%,100% 100%,100% 100%)} }
Single click to play video in an iframe (from default Elementor video element), works for Vimeo, maybe for youtube as well?
document.addEventListener('DOMContentLoaded', function () { const videoContainer = document.querySelector('.videoContainerDesktop'); const videoOverlay = videoContainer.querySelector('.elementor-custom-embed-image-overlay'); const videoContainerMobile = document.querySelector('.videoContainerMobile'); let videoOverlayMobile; if (videoContainerMobile) videoOverlayMobile = videoContainerMobile.querySelector('.elementor-custom-embed-image-overlay'); window.focus(); window.addEventListener("blur", () => { setTimeout(() => { if (document.activeElement.tagName === "IFRAME") { videoOverlay.remove(); if (videoOverlayMobile) videoOverlayMobile.remove(); } }); }, { once: true }); }); /* css */ .videoContainerDesktop .elementor-custom-embed-image-overlay, .videoContainerMobile .elementor-custom-embed-image-overlay { pointer-events: none; }
multi level nav shortcode
function multi_level_nav_shortcode($atts) { $menu = wp_get_nav_menu_object($atts['menu']); $menu_items = wp_get_nav_menu_items($menu->term_id); $menu_id = str_replace(' ', '_', $atts['menu']); // Replace spaces with underscores in the menu ID $menu_list = '<nav id="' . $menu_id . '" class="multiLevelNav">'; $menu_list .= '<ul class="multiLevelNavUl">'; $submenu = false; $submenu_id = 1; foreach ((array) $menu_items as $key => $menu_item) { $title = $menu_item->title; $url = $menu_item->url; $menu_item_id = $menu_item->ID; $parent = $menu_item->menu_item_parent; $classes = implode(" ", $menu_item->classes); $classes .= ' multiLevelNavLi'; if (!$parent) { // If it's a top-level menu item $submenu = false; $submenu_id++; // Check if this menu item has children $has_children = false; foreach ((array) $menu_items as $potential_child) { if ($potential_child->menu_item_parent == $menu_item_id) { $has_children = true; break; } } // Add the 'has-children' class if necessary $link_classes = $has_children ? 'multiLevelNavItem multiLevelNavItem_hasChildren' : 'multiLevelNavItem'; $menu_list .= '<li class="' . $classes . '"><a href="' . $url . '" class="' . $link_classes . '" aria-haspopup="true" aria-controls="submenu-' . $submenu_id . '" aria-expanded="false">' . $title . '</a>'; } if ($parent) { if (!$submenu) { // If it's the first item in the sub-menu $submenu = true; $menu_list .= '<ul id="submenu-' . $submenu_id . '" class="multiLevelNavSubMenu">'; $subitem_counter = 0; // Initialize sub-menu item counter } $subitem_counter++; // Increment the sub-menu item counter $first_subitem_class = $subitem_counter === 1 ? ' multiLevelNavSubMenu_firstLink' : ''; // Check if it's the first item in the sub-menu $menu_list .= '<li class="' . $classes . ' multiLevelNavSubItemLi"><a href="' . $url . '" class="multiLevelNavItem multiLevelNavSubItem' . $first_subitem_class . '">' . $title . '</a></li>'; if (!isset($menu_items[$key + 1]) || $menu_items[$key + 1]->menu_item_parent != $parent) { // If it's the last item in the sub-menu $menu_list .= '</ul>'; $submenu = false; } } } $menu_list .= '</ul></nav>'; return $menu_list; } add_shortcode('multi_level_nav', 'multi_level_nav_shortcode');
multi level nav css and js
<script> document.addEventListener('DOMContentLoaded', function () { let menuItems = document.querySelectorAll('.multiLevelNav .multiLevelNavItem_hasChildren'); let navMenuElement = document.querySelector('.multiLevelNav'); function addClickEventToItems() { menuItems.forEach(menuItem => { let subMenu = menuItem.closest('li').querySelector('.multiLevelNavSubMenu'); subMenu.setAttribute('aria-hidden', 'true'); menuItem.addEventListener('click', function (event) { event.preventDefault(); toggleMenu(this, subMenu); }); menuItem.addEventListener('keydown', function (event) { if (event.key === ' ' || event.key === 'Enter') { event.preventDefault(); toggleMenu(menuItem, subMenu); } }); }); } addClickEventToItems(); // Toggle menu visibility and update ARIA attributes function toggleMenu(menuItem, subMenu) { let shownSubMenu = document.querySelector('.multiLevelNavSubMenu_isShown'); if (shownSubMenu && shownSubMenu !== subMenu) { shownSubMenu.classList.remove('multiLevelNavSubMenu_isShown'); setTimeout(function () { subMenu.classList.add('multiLevelNavSubMenu_isShown'); manageAria(subMenu, menuItem); }, 400); } else { subMenu.classList.toggle('multiLevelNavSubMenu_isShown'); navMenuElement.classList.toggle('multiLevelNav_isExpanded'); manageAria(subMenu, menuItem); } } function closeNavOnFirstLinkClick() { /* for mobile */ let firstLinksInSubMenu = document.querySelectorAll('.multiLevelNavSubMenu_firstLink'); firstLinksInSubMenu.forEach(link => { link.addEventListener('click', function (e) { e.preventDefault(); let subMenu = this.closest('.multiLevelNavSubMenu'); let menuItem = subMenu.closest('li').querySelector('.multiLevelNavItem_hasChildren'); toggleMenu(menuItem, subMenu); }); }); } closeNavOnFirstLinkClick(); function manageAria(subMenu, menuItem) { resetAria(); const isExpanded = subMenu.classList.contains('multiLevelNavSubMenu_isShown'); subMenu.setAttribute('aria-hidden', !isExpanded); menuItem.setAttribute('aria-expanded', isExpanded); } function resetAria() { menuItems.forEach(menuItem => { menuItem.setAttribute('aria-expanded', false); const subMenu = menuItem.closest('li').querySelector('.multiLevelNavSubMenu'); subMenu.setAttribute('aria-hidden', true); }); } function setupImages() { let wrapper = document.querySelector('.multiLevelNavImagesWrapper'); let imagesSrc = [ 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/ksf-a-propos.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/H67-56-copie-4.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/H67-56-copie-3.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/H67-56-copie-2.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/ksf-surf-cours-prive.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/longboard-camp-ksf.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/ksf-surf-avance-2.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/H67-56-copie-1.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/KSF-SUP-FITNESS-9.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/H67-56-copie.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/H67-56-copie-3.jpg', 'https://cdricl52.sg-host.com/wp-content/uploads/2023/05/H67-56-copie-2.jpg',]; /* create images and append to wrapper */ for (let i = 0; i < imagesSrc.length; i++) { let img = document.createElement('img'); img.src = imagesSrc[i]; img.setAttribute('loading', 'lazy'); wrapper.appendChild(img); } } setupImages(); function setupImagesInteraction() { let firstLiInSubMenu = Array.from(document.querySelectorAll('.multiLevelNavSubMenu > li:first-child')); let navItemsWithImages = Array.from(document.querySelectorAll('.multiLevelNavLi')).filter(item => !firstLiInSubMenu.includes(item)); let images = Array.from(document.querySelectorAll('.multiLevelNavImagesWrapper img')); /* ignore first image */ images.shift(); navItemsWithImages.forEach((navItem, i) => { navItem.addEventListener('mouseenter', function () { if (images[i]) images[i].classList.add('multiLevelNavImage_isShown'); }); navItem.addEventListener('mouseleave', function () { if (images[i]) images[i].classList.remove('multiLevelNavImage_isShown'); }); }); } setupImagesInteraction(); }); </script> <style> .navigationWrapper { padding: 100px 8vw 0; /* Adds padding to the navigation wrapper */ } /* Sets the line height and font size for the navigation items */ .multiLevelNav { line-height: 3; font-size: 1.6vw; letter-spacing: 0.2vw; text-transform: uppercase; } .multiLevelNavSubMenu { position: absolute; left: calc(100% + 10vw); /* Positions the sub-menu to the right of the parent item */ top: 0.8vw; opacity: 0; pointer-events: none; transition: opacity 0.4s ease-in-out; /* Adds a transition effect for opacity */ width: 30vw; line-height: 2.7; font-size: 1.4vw; letter-spacing: 0; text-transform: uppercase; /* Sets the line height and font size for the sub-menu items */ } /* Make the sub-menu visible and allows interaction */ .multiLevelNavSubMenu_isShown { pointer-events: all; opacity: 1; } /* Remove default list styles and padding */ .multiLevelNavUl, .multiLevelNavSubMenu { list-style: none; padding: 0; } /* Sets the text color for navigation items */ .multiLevelNavItem.multiLevelNavItem { position: relative; color: #004C5C; } /* Adds a chevron icon for items with a sub-menu */ .multiLevelNavItem.multiLevelNavItem_hasChildren::after { content: ''; --chevron-color: #004C5C; /* color of the chevron */ width: .6vw; height: .6vw; display: block; position: absolute; right: -2vw; top: 50%; border-right: 2px solid var(--chevron-color); border-top: 2px solid var(--chevron-color); transform: translateY(-50%) rotate(45deg); } /* Creates an underline effect on hover or when the item is expanded */ .multiLevelNavItem::before { content: ''; position: absolute; width: 100%; height: 2px; /* How thick the line is */ background-color: #FFD400 !important; /* Color of the line */ transition: transform 0.7s cubic-bezier(1, 0, 0, 1); /* Adjusts the transition duration and timing function */ bottom: -6px; /* Vertical positioning of the line */ opacity: 1; left: 0; z-index: -3; transform: scaleX(0); transform-origin: right; } /* Shows the underline effect on hover or when the item is expanded */ .multiLevelNavItem:hover::before, .multiLevelNavItem[aria-expanded="true"]::before { transform: scaleX(1); transform-origin: left; } /* Sets the height of the images wrapper */ .multiLevelNavImagesWrapper { height: 60vh; } .multiLevelNavImagesWrapper img { position: absolute; object-fit: cover; left: 0; top: 20px; width: 100%; height: 100%; opacity: 0; transition: opacity 0.4s; border-radius: 6px; } img.multiLevelNavImage_isShown { opacity: 1; } /* Make the first image visible by default */ .multiLevelNavImagesWrapper img:first-child { opacity: 1; } /* Hides the first link in the sub-menu, on desktop and tablets */ .multiLevelNavSubMenu_firstLink { display: none; } @media (max-width:1024px) { .navigationWrapper { padding: 100px 4vw 0; /* Adjusts the padding for tablets */ } .multiLevelNav { font-size: 1.8vw; /* Adjusts the font size for tablets */ } .multiLevelNavSubMenu { font-size: 1.6vw; /* Adjusts the font size for sub-menu items on tablets */ } } @media (max-width:767px) { .navigationWrapper { padding: 60px 20px 0; overflow: hidden; /* Adjusts the padding and hides overflow for mobile */ } .multiLevelNav { transition: transform 0.4s; /* Adds a transition effect for the navigation on mobile */ } .multiLevelNav_isExpanded { transform: translateX(-100vw); /* Moves the navigation off-screen when expanded on mobile */ } .multiLevelNav { font-size: 1.5rem; /* Adjusts the font size of first-level menu items on mobile */ } /* Adjusts the styles of the chevron icon on mobile, for first level nav items */ .multiLevelNavItem.multiLevelNavItem_hasChildren::after { width: 10px; height: 10px; right: -20px; } .multiLevelNavSubMenu { left: 100vw; top: 10px; transition: opacity 0.14s ease-in-out; width: calc(100vw - 40px); /* Adjusts the width of the sub-menu on mobile */ line-height: 2.7; font-size: 1.4rem; /* Adjusts the font size of sub-menu items on mobile */ } /* line-height and line spacing on mobile */ .multiLevelNavLi.multiLevelNavSubItemLi { line-height: 1.5; padding: 12px 0; } .multiLevelNavSubMenu_firstLink { display: initial; /* Shows the first link in the sub-menu on mobile */ font-size: 1.2rem; /* Adjusts the font size of the first link on mobile */ margin-left: 30px; opacity: 0.8; } /* Adds a left-pointing chevron to close the sub-menu on mobile */ .multiLevelNavSubMenu_firstLink::after { content: ''; --chevron-color: #004C5C; /* color of the chevron */ width: 10px; height: 10px; position: absolute; left: -20px; top: 50%; border-left: 2px solid var(--chevron-color); border-bottom: 2px solid var(--chevron-color); transform: translateY(-50%) rotate(45deg); background-color: transparent; } .multiLevelNavImagesWrapper { display: none; height: 30vh; /* Adjusts the height of the images on mobile */ } } </style>
Elementor WordPress Default Nav Element, center sub nav perfectly
.full_center_dropdown nav ul .sub-menu.sub-menu.sub-menu { left:50%!important; transform: translateX(-50%); }
Add gradient edge mask to both ends, for both direction
.marqueeWrapper:not(.marqueeVertical) { --mask-size: 0; -webkit-mask: linear-gradient(90deg, transparent, white var(--mask-size), white calc(100% - var(--mask-size)), transparent); } .marqueeVertical.marqueeVertical { --mask-size: 0; -webkit-mask: linear-gradient(0deg, transparent, white var(--mask-size), white calc(100% - var(--mask-size)), transparent); } @media (max-width:1024px) { .marqueeWrapper:not(.marqueeVertical) { --mask-size-tablet: 0; -webkit-mask: linear-gradient(90deg, transparent, white var(--mask-size-tablet), white calc(100% - var(--mask-size-tablet)), transparent); } .marqueeVertical.marqueeVertical { --mask-size-tablet: 0; -webkit-mask: linear-gradient(0deg, transparent, white var(--mask-size-tablet), white calc(100% - var(--mask-size-tablet)), transparent); } } @media (max-width:767px) { .marqueeWrapper:not(.marqueeVertical) { --mask-size-mobile: 0; -webkit-mask: linear-gradient(90deg, transparent, white var(--mask-size-mobile), white calc(100% - var(--mask-size-mobile)), transparent); } .marqueeVertical.marqueeVertical { --mask-size: 0; -webkit-mask: linear-gradient(0deg, transparent, white var(--mask-size-mobile), white calc(100% - var(--mask-size-mobile)), transparent); } }
Elementor File Upload Improved
<style> .elementor-field-type-upload .elementor-screen-only { position: static; width: initial; height: initial; margin: initial; padding: initial; overflow: initial; clip: initial; } .elementor-upload-field { position: absolute; visibility: hidden; opacity: 0; left: -200vw; } .elementor-field-type-upload.elementor-field-type-upload.elementor-field-type-upload.elementor-field-type-upload{ border: 2px dashed #ccc; border-radius: 2px; padding: 20px; margin-left: 5px; margin-top: 25px; margin-bottom: 20px; width: calc(50% - 10px); } .elementor-field-type-upload label { width: 100%; } .fileUpload_labelContent { display: flex; flex-direction: column; text-align: center; align-items: center; font-size: 15px; line-height: 1.5; display: flex; align-items: center; justify-content: center; flex-direction: column; cursor: pointer; } .fileUpload_labelContent.hide { display: none } .fileUpload_uploadHint { color: rgba(0,0,0,0.4) } .fileUpload_labelContent svg { width: 40px; height: 40px; margin-bottom: 5px; } .fileUpload_previewField { width: 100%; padding: 0; display: flex; flex-direction: column; } .fileUpload_filePreview { display: flex; align-items: center; margin-top: 10px; position: relative; width: 100%; padding: 10px; } .fileUpload_fileName{ margin-bottom: 10px; overflow: hidden; text-overflow: ellipsis; color: #ffffff; } img.fileUpload_previewImage { width: 50px; height: 50px; object-fit: cover; margin: 10px; } .fileUpload_fileInfo { margin: 10px; display: flex; flex-direction: column; max-width: 60%; text-overflow: ellipsis; } .fileUpload_previewIcon { background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M20 9a1.3 1.3 0 0 0 0-.3v-.1a1 1 0 0 0-.2-.3l-6-6a1 1 0 0 0-.3-.2h-.1L13 2H7a3 3 0 0 0-3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3V9Zm-6-3.6L16.6 8H14ZM18 19a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5v5a1 1 0 0 0 1 1h5Z' fill='%23b1b1b1'/%3E%3C/svg%3E"); background-size: contain; width: 50px; height: 50px; margin: 10px; background-repeat: no-repeat; } .fileUpload_fileSize { font-weight: 500; font-family: 'Roboto'; margin-bottom: 4px; color:#999999; } .fileUpload_removeFile{ position: absolute; top: 10px; right: 14px; cursor: pointer; width: 21px; height: 21px; opacity: 0.5; } .fileUpload_removeFile:hover { opacity: 1; } .fileUpload_removeFile:before, .fileUpload_removeFile:after { position: absolute; left: 15px; content: ' '; height: 14px; width: 2px; background-color: #777777; } .fileUpload_removeFile:before { transform: rotate(45deg); } .fileUpload_removeFile:after { transform: rotate(-45deg); } </style> <script> (function () { let uploadMessage = `Drop files to attach, or browse`; let uploadHint = ``; let iconSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#b1b1b1" d="M12.7 10.8a1 1 0 0 0-.3-.2 1 1 0 0 0-.8 0 1 1 0 0 0-.3.2l-2 2a1 1 0 0 0 1.4 1.4l.3-.3v2.6a1 1 0 0 0 2 0v-2.6l.3.3a1 1 0 0 0 1.4 0 1 1 0 0 0 0-1.4ZM19 5.5h-6.3l-.3-1a3 3 0 0 0-2.8-2H5a3 3 0 0 0-3 3v13a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-10a3 3 0 0 0-3-3Zm1 13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-13a1 1 0 0 1 1-1h4.6a1 1 0 0 1 1 .7l.5 1.6a1 1 0 0 0 .9.7h7a1 1 0 0 1 1 1Z"/></svg>`; let labelFieldContent = `<div class="fileUpload_labelContent">${iconSVG}<span class="fileUpload_uploadMessage">${uploadMessage}</span><span class="fileUpload_uploadHint">${uploadHint}</span></div>`; let uploadFieldElems = document.querySelectorAll('form .elementor-field-type-upload'); uploadFieldElems.forEach(init); /* initiate in popups */ document.addEventListener('DOMContentLoaded', function () { jQuery(document).on('elementor/popup/show', () => { let uploadFieldElems = document.querySelectorAll('.elementor-popup-modal .elementor-field-type-upload'); uploadFieldElems.forEach(init); }); }); function init(uploadField) { if (uploadField.classList.contains('upload_init')) { return; } uploadField.classList.add('upload_init'); let formLabel = uploadField.querySelector('label'); formLabel.innerHTML = labelFieldContent; let fileUploadInput = uploadField.querySelector('input'); let uploadPreviewField = document.createElement('ul'); uploadPreviewField.classList.add('fileUpload_previewField'); const dtStateTracker = new DataTransfer(); fileUploadInput.insertAdjacentElement('afterEnd', uploadPreviewField); fileUploadInput.addEventListener('change', manageUpload); uploadField.addEventListener("drop", (event) => { if (event.dataTransfer.files.length) { console.log(event.dataTransfer.files); addFileToFileList(event.dataTransfer.files); manageUpload("drop"); } }); function updateStateTracker() { dtStateTracker.clearData(); for (let i = 0; i < fileUploadInput.files.length; i++) { let file = fileUploadInput.files[i] dtStateTracker.items.add(file); } } function manageUpload(origin) { if (origin !== "drop") { mergeFilesLists(); } updateList(); updateStateTracker(); } function updateList() { uploadPreviewField.innerHTML = ''; for (let i = 0; i < fileUploadInput.files.length; i++) { let file = fileUploadInput.files.item(i); let uploadedFileItem = document.createElement('li'); uploadedFileItem.classList.add('fileUpload_filePreview'); if (file.type.startsWith("image/")) { let previewImage = new Image(); previewImage.classList.add("fileUpload_previewImage"); previewImage.src = URL.createObjectURL(file); uploadedFileItem.append(previewImage); } else { let fileIcon = document.createElement('i'); fileIcon.classList.add("fileUpload_previewIcon"); uploadedFileItem.append(fileIcon); } let fileInfo = document.createElement('div'); fileInfo.classList.add('fileUpload_fileInfo'); fileInfo.appendChild(getFileSize(file)); let fileName = document.createElement('span'); fileName.classList.add('fileUpload_fileName'); fileName.innerHTML = file.name; fileInfo.appendChild(fileName); uploadedFileItem.appendChild(fileInfo); let removeFileItem = document.createElement('span'); removeFileItem.classList.add('fileUpload_removeFile') removeFileItem.addEventListener('click', function (event) { let listItem = event.target.closest('.fileUpload_filePreview'); let idx = Array.from(uploadField.querySelectorAll('.fileUpload_filePreview')).indexOf(listItem); listItem.remove(); removeFileFromFileList(idx); updateStateTracker(); }); uploadedFileItem.appendChild(removeFileItem); uploadPreviewField.appendChild(uploadedFileItem); } } function mergeFilesLists() { const dt = new DataTransfer; for (let i = 0; i < dtStateTracker.files.length; i++) { const file = dtStateTracker.files[i]; dt.items.add(file); } for (let i = 0; i < fileUploadInput.files.length; i++) { const file = fileUploadInput.files[i]; dt.items.add(file); } fileUploadInput.files = dt.files; } function removeFileFromFileList(index) { /* from https://stackoverflow.com/questions/3144419/how-do-i-remove-a-file-from-the-filelist */ const dt = new DataTransfer(); const input = fileUploadInput; const { files } = input; for (let i = 0; i < files.length; i++) { const file = files[i] if (index !== i) { dt.items.add(file); } } input.files = dt.files; } function addFileToFileList(newFilesTransfer) { const dt = new DataTransfer(); const input = fileUploadInput; const { files } = input; for (let i = 0; i < files.length; i++) { const file = files[i] dt.items.add(file); } for (let i = 0; i < newFilesTransfer.length; i++) { const file = newFilesTransfer[i] dt.items.add(file); } input.files = dt.files; } ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { uploadField.addEventListener(eventName, preventDefaults, false) }) function preventDefaults(e) { e.preventDefault() e.stopPropagation() } function getFileSize(file) { let suffix = "bytes"; let size = file.size; if (size >= 1024 && size < 1024000) { suffix = "KB"; size = Math.round(size / 1024 * 100) / 100; } else if (size >= 1024000) { suffix = "MB"; size = Math.round(size / 1024000 * 100) / 100; } let fileSize = document.createElement('span'); fileSize.classList.add('fileUpload_fileSize'); fileSize.innerHTML = `<strong>${size}</strong> ${suffix}`; return fileSize; } }; }()); </script>
Smooth CSS transition between two logos, or even text and logo, as it is for Elementor sticky header, CSS on the parent container that's sticky
selector .oldlogo { opacity:1; transition: transform 0.3s, opacity 0.4s; } selector.elementor-sticky--effects .oldlogo { opacity:0; transition: transform 0.3s, opacity 0.3s; transform: scale3d(0.6, 0.9, 1); } selector .newlogo { position: absolute; left:50%; top:50%; transform:translate(-50%,-50%) scale3d(2.6, 0.6, 1); width:100%; transition: transform 0.3s, opacity 0.3s; opacity:0; } selector.elementor-sticky--effects .newlogo { transform:translate(-50%,-50%) scale3d(1, 1, 1); opacity:1; }
Convert links to youtube videos to the Elementor Lightbox automatically
<script> (function(){ const links = document.querySelectorAll('.e-loop-item a'); links.forEach((link,i) => { const href = link.getAttribute('href'); /* Check if the href contains a YouTube link */ if (href && href.includes('youtube.com/')) { /* Extract the YouTube video ID */ let videoId = href.split('v=')[1]; const ampersandPosition = videoId.indexOf('&'); if (ampersandPosition !== -1) { videoId = videoId.substring(0, ampersandPosition); } /* Create the new embed URL */ const embedUrl = `https://www.youtube.com/embed/${videoId}?feature=oembed&autoplay=1&rel=0`; /* Set the required lightbox attributes */ link.setAttribute('data-elementor-lightbox-video', embedUrl); link.setAttribute('data-elementor-lightbox-slideshow', 'lightbox' + i); /* adjust this if some videos should share the same lightbox, they will need the same value here */ link.setAttribute('data-elementor-open-lightbox', 'yes'); /* empty href to prevent bug */ link.setAttribute('href',''); } }); }()); </script> <style> .elementor-lightbox .swiper-wrapper { cursor: default !important; } .elementor-lightbox.elementor-lightbox .swiper .swiper-pagination-fraction { display: none; } </style>
Arrow position on SwiperJS Swipers (ready to be copy pasted in a tutorial)
<h2>Elementor Loop Carousel Change Arrows Position</h2> Let's move these Loop Carousel arrows somewhere different than to either side, shall we? It's quite simple: copy paste the code snippet that you need into your page. <h3>Move the Loop Carousel arrows to the top right corner</h3> <div class="copy_text_box"> <pre class="code_text">/* Code from https://element.how/elementor-loop-carousel-premium-mods/ * Move the Loop Carousel arrows to the top right corner */ .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-prev, .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { position: absolute; z-index: 11; left: initial; right: 30px; top: -20px } .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { right: 0; } </pre> </div> <h3>Move the Loop Carousel arrows to the bottom left corner</h3> <div class="copy_text_box"> <pre class="code_text">/* Code from https://element.how/elementor-loop-carousel-premium-mods/ * Move the Loop Carousel arrows to the bottom left corner */ .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-prev, .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { position: absolute; z-index: 11; top: initial; left: 0; right: initial; bottom: -20px; } .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { left: 30px; } </pre> </div> <h3>Move the Loop Carousel arrows to the top left corner</h3> <div class="copy_text_box"> <pre class="code_text">/* Code from https://element.how/elementor-loop-carousel-premium-mods/ * Move the Loop Carousel arrows to the top left corner */ .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-prev, .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { position: absolute; z-index: 11; right: initial; left: 0; top: -20px; } .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { left: 30px; } </pre> </div> <h3>Move the Loop Carousel arrows to the bottom right corner</h3> <div class="copy_text_box"> <pre class="code_text">/* Code from https://element.how/elementor-loop-carousel-premium-mods/ * Move the Loop Carousel arrows to the bottom right corner */ .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-prev, .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { position: absolute; z-index: 11; left: initial; right: 34px; top: initial; bottom: -44px; } .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { right: 0; } </pre> </div> <h3>Move the Loop Carousel arrows to the middle below the slider</h3> <div class="copy_text_box"> <pre class="code_text">/* Code from https://element.how/elementor-loop-carousel-premium-mods/ * Move the Loop Carousel arrows to the middle below the slider */ .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-prev, .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { position: absolute; z-index: 11; left: calc(50% - 20px); right: initial; top: initial; bottom: -30px; transform: translateX(-50%); } .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { left: calc(50% + 20px); } </pre> </div> [click_to_expand title="Note that, as is, the code snippets will affect the position of all arrows on your page. Here is how to prevent that."] To single one carousel out, you can add the code under Advanced > Custom CSS for that particular slider, and prefix the three selectors with the keyword "selector". Here is an example with the "middle" position. <div class="copy_text_box"> <pre class="code_text">/* Code from https://element.how/elementor-loop-carousel-premium-mods/ * Move the Loop Carousel arrows to the middle below the slider */ selector .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-prev, selector .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { position: absolute; z-index: 11; left: calc(50% - 20px); right: initial; top: initial; bottom: -30px; transform: translateX(-50%); } selector .elementor-swiper-button.elementor-swiper-button.elementor-swiper-button.elementor-swiper-button-next { left: calc(50% + 20px); } </pre> </div> [/click_to_expand] Changing the position of the arrow is compatible with the other mods below. <h3>Adjusting the Elementor loop carousel arrows position CSS code</h3> In the code you will see two CSS rules. The first one adjusts the vertical position of both arrows, and the exact position of the left arrow. The other CSS rule adjusts the position of the right arrow. In short, the CSS declarations with the value "initial", don't change them. Those with a value set in PX, adjust to your liking, to position your arrows better! Same goes for the "middle" option. Only change the PX values, keep the rest as is.
Elementor new tabs element open and scroll to top of element on click of tab title on desktop
/* automatically scroll back up to the top of the tabs element after changing tab, useful if tabs titles are sticky, otherwise delete */ Array.from(document.querySelectorAll('.e-n-tabs-heading > .e-n-tab-title')).filter(e => e.parentElement.closest('.e-n-tab-title') == null).forEach(function(tab) { tab.addEventListener('click', function() { if (!this.closest('.e-n-tabs')) return; let offsetValue = 140; let targetOffsetTop = this.closest('.e-n-tabs').getBoundingClientRect().top + window.scrollY - offsetValue; window.scrollTo({ top: targetOffsetTop, behavior: 'smooth' }); }); }); /* END OF automatic scroll back up tabs element */
Horizontal Scroll Section (free tutorial) make it scroll until the first card that's partly in view, as it is work if the 'cards' are direct children of 'horizontale'
document.addEventListener("DOMContentLoaded", function () { function init(isPopup) { let horizons; let leftArrow; let rightArrow; if (isPopup) { horizons = document.querySelectorAll('.dialog-lightbox-widget .horizontale'); leftArrow = document.querySelectorAll('.dialog-lightbox-widget .leftarrow'); rightArrow = document.querySelectorAll('.dialog-lightbox-widget .rightarrow'); } else { horizons = document.querySelectorAll('.horizontale:not(.horizontal-init)'); leftArrow = document.querySelectorAll('.leftarrow:not(.horizontal-init)'); rightArrow = document.querySelectorAll('.rightarrow:not(.horizontal-init)'); } horizons.forEach((horizon, i) => { let isDown = false; let startX; let scrollLeft; horizon.classList.add('horizontale-init'); leftArrow[i].classList.add('horizontale-init'); rightArrow[i].classList.add('horizontale-init'); let cards = horizon.querySelectorAll('.horizontale > div'); let containerRect = horizon.getBoundingClientRect(); let scrollToCardIndex = 0; let cardsThatFitInAContainer = Math.floor(containerRect.width / cards[0].offsetWidth); window.addEventListener('resize', function () { containerRect = horizon.getBoundingClientRect(); cardsThatFitInAContainer = Math.floor(containerRect.width / cards[0].offsetWidth); }); function findRightScrollPos() { for (let i = 0; i < cards.length; i++) { const cardRect = cards[i].getBoundingClientRect(); if (cardRect.right >= containerRect.right) { scrollToCardIndex = i; break; } } scrollAction(); } function findLeftScrollPos() { for (let i = cards.length - 1; i >= 0; i--) { const cardRect = cards[i].getBoundingClientRect(); if (cardRect.left < containerRect.left) { scrollToCardIndex = i; break; } } scrollToCardIndex = scrollToCardIndex - cardsThatFitInAContainer + 1; if (scrollToCardIndex < 0) { scrollToCardIndex = 0; } scrollAction(); } function scrollAction() { if (scrollToCardIndex < cards.length) { const scrollToCard = cards[scrollToCardIndex]; const scrollOffset = scrollToCard.offsetLeft - 20; horizon.scrollTo({ left: scrollOffset, behavior: 'smooth' }); } } if (leftArrow[i]) { leftArrow[i].setAttribute('tabindex', '0'); rightArrow[i].setAttribute('tabindex', '0'); leftArrow[i].addEventListener('click', function () { findLeftScrollPos(); }); rightArrow[i].addEventListener('click', function () { findRightScrollPos(); }); } const links = horizon.querySelectorAll('.horizontale > div'); horizon.addEventListener('mousedown', (e) => { isDown = true; horizon.classList.add('active'); startX = e.pageX - horizon.offsetLeft; scrollLeft = horizon.scrollLeft; }); horizon.addEventListener('mouseleave', () => { isDown = false; horizon.classList.remove('active'); for (var i = 0; i < links.length; i++) { links[i].classList.remove('noclick'); } }); horizon.addEventListener('mouseup', () => { isDown = false; horizon.classList.remove('active'); for (var i = 0; i < links.length; i++) { links[i].classList.remove('noclick'); } }); horizon.addEventListener('mousemove', (e) => { if (!isDown) return; e.preventDefault(); const x = e.pageX - horizon.offsetLeft; const walk = (x - startX) * 1.6; horizon.scrollLeft = scrollLeft - walk; for (var i = 0; i < links.length; i++) { links[i].classList.add('noclick'); } }); }); } init(); if (typeof jQuery === 'function') { jQuery(document).on('elementor/popup/show', () => { init(true); }); } });
Window resize width event listener
let windowsWidth = window.innerWidth; window.addEventListener("resize", function() { if (windowsWidth !== window.innerWidth){ //work here windowsWidth = window.innerWidth; } });
isMoble or not with CSS media queries and JS
/* check if device has hover capabilities (ie is a mobile), checking the max width media query is required also because Samsung devices wrongly return they are (hover:hover) devices, more info: https://www.ctrl.blog/entry/css-media-hover-samsung.html */ let noHover = window.matchMedia('(hover:none)').matches || window.matchMedia('(max-width:1024px)').matches; let hasHover = !noHover; let devToolMQ = window.matchMedia('(hover:none)'); devToolMQ.onchange = () => { /* reload page for people looking in the devtool mobile preview mode */ window.location = window.location.href; }
Make Clickable Element automatically 44px click size, at a minimum, useful for mobile click target size
.click-target-helper::after { --click-target-minimum: 44px; --inset-by: min( 0px, calc((100% - var(--click-target-minimum)) / 2) ); content: ""; position: absolute; top: var(--inset-by); left: var(--inset-by); right: var(--inset-by); bottom: var(--inset-by); } /* Highlight the pseudoelement to reveal the tap target size. */ .click-target-helper::after { outline: 1px solid red; }
Find first focusable element and focus on it
function focusFirstFocusableElement(parentElement) { let focusableElements = ` a[href], area[href], input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"])`; let focusable = Array.from(parentElement.querySelectorAll(focusableElements)); let firstFocusableElement = focusable.find(el => { let computedStyle = window.getComputedStyle(el); return computedStyle.display !== 'none' && computedStyle.visibility !== 'hidden'; }); if (firstFocusableElement) { firstFocusableElement.focus(); } }
Vanilla JS way to keep accordion stable as they open and close (keep the viewport position where the clicked element is)
document.addEventListener("DOMContentLoaded", function() { const accordionTitles = document.querySelectorAll('.e-n-accordion-item-title'); accordionTitles.forEach(title => { title.addEventListener('click', function(event) { const clickedElement = event.target; const initialTopOffset = clickedElement.getBoundingClientRect().top; let startTime; function scrollAnimation(currentTime) { if (!startTime) startTime = currentTime; const elapsedTime = currentTime - startTime; if (elapsedTime < 600) { /* 0.6s duration */ const currentTopOffset = clickedElement.getBoundingClientRect().top; const difference = currentTopOffset - initialTopOffset; window.scrollBy(0, difference); requestAnimationFrame(scrollAnimation); } } requestAnimationFrame(scrollAnimation); }); }); });
Low opacity frame border on section / container
.fancyBorder{ position:absolute; width:100%; height:100%; left:0; top:0; background-color:#33333344; z-index:8; mix-blend-mode: multiply; } .fancyBorder::after { content:''; position:absolute; left:40px; top:40px; right:40px; bottom:40px; background-color:#FFFFFF; z-index:9; border-radius:100px; } <script> let borderedSection = document.querySelector('.bordered-section'); let fancyBorderDiv = document.createElement('div'); fancyBorderDiv.classList.add('fancyBorder'); borderedSection.appendChild(fancyBorderDiv); </script> <div class="fancyBorder"></div>
Add scrolling effect parallax based on scrolling out of view container
document.addEventListener('DOMContentLoaded', function () { let animOnScrollContainers = document.querySelectorAll('.hero-section'); animOnScrollContainers.forEach((animOnScrollContainer) => { let scrolledRatio = 0; let isUpdateScheduled = false; function manageScroll() { let animContainerBottom = animOnScrollContainer.getBoundingClientRect().bottom; let windowHeight = window.innerHeight; scrolledRatio = animContainerBottom / windowHeight; /* clamp scrolledRatio in between 0 and 1 */ scrolledRatio = Math.min(Math.max(scrolledRatio, 0), 1); animOnScrollContainer.style.setProperty('--scrolled-ratio', scrolledRatio); isUpdateScheduled = false; }; window.addEventListener('scroll', () => { if (!isUpdateScheduled) { isUpdateScheduled = true; requestAnimationFrame(manageScroll); } }); }); }); /* transform: translateY( calc( ( 1 - var(--scrolled-ratio) ) * -70px)) */
Shortcode to get the full post content
function current_post_content_shortcode() { // If in the admin dashboard, return early. if (is_admin()) { return ''; } // If in Elementor edit mode, return early. if (\Elementor\Plugin::$instance->editor->is_edit_mode() || \Elementor\Plugin::$instance->preview->is_preview_mode() ) { return; } // Get the content of the current post. $content = get_the_content(); // Apply the 'the_content' filter to ensure shortcodes and other content filters are processed. $content = apply_filters('the_content', $content); // Use the wpautop() function to format the content with paragraphs. $content = wpautop($content); return $content; } add_shortcode('current_post_content', 'current_post_content_shortcode');
Button that fills in from left to right with a colored background
/* card button-orange styles */ .card-button-orange .elementor-button { position: relative; transition: background-position 0.5s; --bg-color: #BC7845; border: 2px solid var(--bg-color); background-image: radial-gradient(var(--bg-color) 50%, transparent 50%), linear-gradient(to right, var(--bg-color) 50%, transparent 50%); background-size: 60% 170%, 260% 100%; background-repeat: no-repeat; background-position: -150% center, 100% center; } @media (hover:hover) and (pointer:fine) { .card-button-orange .elementor-button:hover { background-position: 230% center, 0% center; } } /* END OF card button styles */
Improved Elementor WordPress Nav element active stylings
/* Code from https://element.how/elementor-table-of-content-improved-active-styles/ * Copyright 2023 Element.How * Licensed for personal and commercial use for customers who purchased access * No redistribution or resale allowed */ /* anchors needs to be in a div with class anchorsWrapper */ /* adjust navElems as needed */ /* works with the Elementor WordPress Menu element */ window.addEventListener('load', function () { setTimeout(function () { setIO(); }, 900); }); function setIO() { let navElems = document.querySelectorAll('[data-elementor-type="wp-page"] .elementor-nav-menu__container'); navElems.forEach((navElem) => { /* make a clone of the nav menu, insert it at the same place, to remove all javascript event listeners */ let navElemClone = navElem.cloneNode(true); navElem.parentNode.replaceChild(navElemClone, navElem); /* get all anchors from the nav menu */ let navItems = navElemClone.querySelectorAll('.elementor-item-anchor'); /* get all anchors from the page, that match those found in navItems links */ let divAnchors = document.querySelector('.anchorsWrapper').querySelectorAll('.elementor-element[id]'); divAnchors.forEach((anchor) => { const options = { threshold: 0, rootMargin: "10px 0px -30% 0px", }; const callback = (entries, observer) => { entries.forEach((entry) => { const index = Array.from(divAnchors).indexOf(anchor); const currentY = entry.boundingClientRect.y; /* scrolling down and intersecting */ if (entry.isIntersecting && entry.boundingClientRect.top > 100) { navItems.forEach(e => { e.classList.remove('elementor-item-active'); e.setAttribute('aria-current', ''); }); navItems[index].classList.add('elementor-item-active'); navItems[index].setAttribute('aria-current', 'location'); } else if (!entry.isIntersecting && entry.boundingClientRect.top > 100) { /* scrolling up and not intersecting */ navItems.forEach(e => { e.classList.remove('elementor-item-active'); e.setAttribute('aria-current', ''); }); if (index == 0) { navItems[0].classList.add('elementor-item-active') } else { navItems[index - 1].classList.add('elementor-item-active'); navItems[index - 1].setAttribute('aria-current', 'location'); } } }); }; const observer = new IntersectionObserver(callback, options); observer.observe(anchor); }); }); }
Open all images in a Carousel Element in the same lightbox
/* open all the images that are in a given Carousel element in the same lightbox, they need to be set to open in a lightbox in the elementor editor */ window.addEventListener('load', () => { let carouselElems = document.querySelectorAll('.e-n-carousel'); carouselElems.forEach((e, i) => { e.querySelectorAll('a[data-elementor-open-lightbox="yes"]').forEach(anchor => anchor.setAttribute('data-elementor-lightbox-slideshow', '34533' + i)); }); });
Fix the bug where elements over a video on Safari and iOS will flicker, the fix is for the horizontal containres in particular, with an inner container added within each 'slide container'
Stuff over the video disappearing I was able to replicate this on iOS and even on MacOS Safari! That was a tough one... clearly a render bug with the video. Anyhow I was able to find a fix: .horizontalScrollTranslate > .e-con > .e-con-inner > .e-con, .horizontalScrollTranslate .elementor-background-video-container::before { transform: translateZ(0px); }
Gradient on start and end of an element
selector .swiper, selector .swiper-container { --mask-width: 140px; -webkit-mask-image: -webkit-gradient(linear, right top, left top, color-stop(21%, rgba(0,0,0,1)), color-stop(99%, rgba(0,0,0,0))), -webkit-gradient(linear, left top, right top, from(#000000), to(#000000)), -webkit-gradient(linear, left top, right top, color-stop(21%, rgba(0,0,0,1)), color-stop(99%, rgba(0,0,0,0))); -webkit-mask-image: linear-gradient(270deg, rgba(0,0,0,1) 21%, rgba(0,0,0,0) 99%), linear-gradient(90deg, #000000 0%, #000000 100%), linear-gradient(90deg, rgba(0,0,0,1) 21%, rgba(0,0,0,0) 99%); -webkit-mask-position: left center,center center, right center; -webkit-mask-size: var(--mask-width) 100%, calc(101% - (2* var(--mask-width))), var(--mask-width) 100%; -webkit-mask-repeat: no-repeat; }
Fade in up entrance animation that plays letter by letter, but all ends at the same time
/* .parallaxh2, .subtitle, .boxTop, .boxMid, .boxBottom */ window.addEventListener('load', function () { let parallaxElems = document.querySelectorAll('.parallaxh2, .subtitle, .boxTop, .boxMid, .boxBottom'); /* use intersection observer to add the class 'parallaxActive' when a parallaxElem is in the viewport */ let observer = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { entry.target.classList.add('parallaxActive'); } }); } , { rootMargin: '-200px 0px -100px 0px', threshold: 0.1 }); parallaxElems.forEach(function (parallaxElem) { observer.observe(parallaxElem); } ); document.querySelectorAll('h2').forEach((header) => { // Split the header by '<br>' and spaces let lines = header.innerHTML.trim().split('<br class="desktop-break">'); header.textContent = ''; let totalDuration = 1; // total animation duration in seconds let spans = []; lines.forEach((line, lineIndex) => { let words = line.split(' '); let lineElement = document.createElement('div'); words.forEach((word, wordIndex) => { let letters = Array.from(word); letters.forEach((letter, index) => { let span = document.createElement('span'); span.innerHTML = letter === ' ' ? ' ' : letter; span.style.display = 'inline-block'; spans.push(span); lineElement.appendChild(span); }); // If not the last word, add a space if (wordIndex !== words.length - 1) { let spaceSpan = document.createElement('span'); spaceSpan.innerHTML = ' '; spans.push(spaceSpan); lineElement.appendChild(spaceSpan); } }); header.appendChild(lineElement); }); let delayStep = totalDuration / spans.length; spans.forEach((span, index) => { span.style.animationDelay = `${index * delayStep}s`; span.style.animationDuration = `${totalDuration - index * delayStep}s`; }); }); }); /* @keyframes fadeInUp { 0% { opacity: 0; transform: translateY(60px); } 100% { opacity: 1; transform: translateY(0); } } .parallaxh2 span{ opacity: 0; display: inline-block; } .parallaxh2.parallaxActive span{ animation-name: fadeInUp; animation-fill-mode: forwards; } */
Aria label fix for Elementor loop link that allows for dynamic aria labels set on the links Accessibility
<script> /* Edit Button/Advanced/Attributes I have set a custom attribute of Post Title and a Before label of aria-label| Create a new code snippet in WP > Elementor > Custom Codes Set the Location to <body> end Add the code Save & Publish Now in your loop template, give your button the class "move-aria-to-link" under Advanced > CSS classes */ /* Find all elements with class "move-aria-to-link" */ let ariaNeedsToBeMovedElems = document.querySelectorAll('.move-aria-to-link'); ariaNeedsToBeMovedElems.forEach(elem => { if (elem.hasAttribute('aria-label')) { let ariaLabel = elem.getAttribute('aria-label'); let anchorElement = elem.querySelector('a'); if (anchorElement) anchorElement.setAttribute('aria-label', ariaLabel); /* Remove the aria-label attribute from the original element */ elem.removeAttribute('aria-label'); } }); </script>
Make animations work in content switchers when it goes to display:block
/* Make animations work in content switchers */ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)"); if (!mediaQuery.matches) { let dataSettingsElems = document.querySelectorAll('[data-settings]'); dataSettingsElems.forEach(elem => { if (elem.dataset.settings.indexOf('_animation_delay') > 1) { elem.style.opacity = '0'; elem.style.animationFillMode = 'forwards'; let animDelay = JSON.parse(elem.dataset.settings)._animation_delay; elem.style.animationDelay = animDelay + 'ms'; } else if (elem.dataset.settings.indexOf('animation_delay') > 1) { elem.style.opacity = '0'; elem.style.animationFillMode = 'forwards'; let animDelay = JSON.parse(elem.dataset.settings).animation_delay; elem.style.animationDelay = animDelay + 'ms'; } }); }
Play any Lottie animation backward
let anim = lottie.getRegisteredAnimations()[0]; /* presuming we want this on the first lottie on the page */ anim.goToAndStop(anim.totalFrames, true); anim.setDirection(-1); anim.play();
Swiper JS and Elementor Recent / Modern way to add functionality to events
<style> body:not(.elementor-editor-active) .biographyCard.biographyCard { position: absolute; pointer-events: none; opacity: 0; transition: 0.4s opacity; } body:not(.elementor-editor-active) .biographyCard.biographyCard.isActive { opacity: 1; pointer-events: all; } .bioCardSwiperPagination a { opacity: 0.6; transition: 0.4s opacity; } .bioCardSwiperPagination a.isActive { opacity: 1; } </style> <script> window.addEventListener('load',function(){ let bioCardSwiper = document.querySelector('.bioCardSwiper .swiper, .bioCardSwiper .swiper-container'); let checkElementorFrontendInterval = setInterval(function() { if (typeof bioCardSwiper.swiper !== 'undefined') { clearInterval(checkElementorFrontendInterval); let swiperInstance = bioCardSwiper.swiper; let bioCardSwiperPagination = document.querySelectorAll('.bioCardSwiperPagination a'); bioCardSwiperPagination[0].classList.add('isActive'); let bioCards = document.querySelectorAll('.biographyCard'); bioCards[0].classList.add('isActive'); bioCardSwiperPagination.forEach((pagination, i) => { pagination.addEventListener('click',function(e){ e.preventDefault(); swiperInstance.slideTo(i); }) }) swiperInstance.on('realIndexChange', function(){ let idx = swiperInstance.realIndex; bioCards.forEach((card,i) => { card.classList.remove('isActive'); if (i == idx) card.classList.add('isActive'); }); bioCardSwiperPagination.forEach((pagination,i) => { pagination.classList.remove('isActive'); if (i == idx) pagination.classList.add('isActive'); }) }); } }, 100); }); </script>
Background Image Slow Zoom In CSS Zoomin
.elementor-background-overlay { transition: transform 5.5s linear!important; transform: scale(1); } .elementor-background-overlay.zoomin { transform: scale(1.3); }
Elementor mega menu add scroll to sub nav for smaller viewports when it doesnt fit entirely
.e-n-menu-item-title +.e-con { max-height: calc(100vh - 120px); /* 120px represents the approximate height of the header */ overflow-y: auto; }
Wait until a variableis set or a condition is met before running a function
let int = setInterval(() => { try { if (document.querySelector('.swiper-container').swiper) { clearInterval(int); init(); } } catch (err) { console.log(err); } }, 400);
CSS only for Safari show this element not that one
.safari-browser { display:none; } /* show safari-browser class elements only on safari browsers, hide not-safari class elements on safari browsers*/ @supports (font: -apple-system-body) and (-webkit-appearance: none) { .not-safari { display:none; } .safari-browser { display:inline; } }
Remove srcset and sizes on images with class name no-srcset
<script> document.addEventListener('DOMContentLoaded', () => { let images = document.querySelectorAll('.no-srcset img'); images.forEach(image => { image.removeAttribute('srcset'); image.removeAttribute('sizes'); }); }); </script>
extract value from string, as well as prefixes and suffixes
function extractValue(str, type) { let value; switch (type) { case 'prefix': // Extract prefix using regex const prefixMatch = str.match(/^([^\d]+)/); value = prefixMatch ? prefixMatch[0] : ''; break; case 'suffix': // Extract suffix using regex const suffixMatch = str.match(/([^\d]+)$/); value = suffixMatch ? suffixMatch[0] : ''; break; case 'numeric': // Extract numeric value using regex const numericMatch = str.match(/(\d+(\.\d+)?)/); value = numericMatch ? parseFloat(numericMatch[0]) : NaN; break; default: value = NaN; } return value; } // Example usage: console.log(extractValue('50%', 'prefix')); // Output: '' console.log(extractValue('50%', 'suffix')); // Output: '%' console.log(extractValue('50%', 'numeric')); // Output: 50 console.log(extractValue('$40', 'prefix')); // Output: '$' console.log(extractValue('$40', 'suffix')); // Output: '' console.log(extractValue('$40', 'numeric')); // Output: 40
jQuery UI Easing functions to extend jQuery's defaults
/* jQuery UI Easing functions to extend jQuery's defaults */ (function () { /* Based on easing equations from Robert Penner (http://www.robertpenner.com/easing) */ var baseEasings = {}; jQuery.each(["Quad", "Cubic", "Quart", "Quint", "Expo"], function (i, name) { baseEasings[name] = function (p) { return Math.pow(p, i + 2); }; }); jQuery.extend(baseEasings, { Sine: function (p) { return 1 - Math.cos(p * Math.PI / 2); }, Circ: function (p) { return 1 - Math.sqrt(1 - p * p); }, Elastic: function (p) { return p === 0 || p === 1 ? p : -Math.pow(2, 8 * (p - 1)) * Math.sin(((p - 1) * 80 - 7.5) * Math.PI / 15); }, Back: function (p) { return p * p * (3 * p - 2); }, Bounce: function (p) { var pow2, bounce = 4; while (p < ((pow2 = Math.pow(2, --bounce)) - 1) / 11) { } return 1 / Math.pow(4, 3 - bounce) - 7.5625 * Math.pow((pow2 * 3 - 2) / 22 - p, 2); } }); jQuery.each(baseEasings, function (name, easeIn) { jQuery.easing["easeIn" + name] = easeIn; jQuery.easing["easeOut" + name] = function (p) { return 1 - easeIn(1 - p); }; jQuery.easing["easeInOut" + name] = function (p) { return p < 0.5 ? easeIn(p * 2) / 2 : 1 - easeIn(p * -2 + 2) / 2; }; }); })();
ACF Options page and add them to WP rest API
if( function_exists('acf_add_options_page') ) { acf_add_options_page(array( 'page_title' => 'Theme General Settings', 'menu_title' => 'Theme Settings', 'menu_slug' => 'theme-general-settings', 'capability' => 'edit_posts', 'redirect' => false )); } /* add options page to rest api */ add_action("rest_api_init", function () { register_rest_route("options", "/all", [ "methods" => "GET", "callback" => "acf_options_route", ]); }); function acf_options_route() { return get_fields('options'); }
Vanilla JS fadeOut()
function fadeOut(el) { el.style.opacity = 1; (function fade() { if ((el.style.opacity -= 0.03) < 0) { passwordOverlay.remove(); } else { requestAnimationFrame(fade); } })(); }
Scrolled Ratio Simple Setup to add the scrolled ratio variable to the target element
document.addEventListener('DOMContentLoaded', function () { let stepsWrappers = document.querySelectorAll('.steps'); stepsWrappers.forEach((stepsWrapper) => { let scrolledRatio = 0; let switchScrollHeight; function updateValues() { switchScrollHeight = stepsWrapper.offsetHeight; } updateValues(); window.addEventListener('resize', () => { updateValues(); }); let isUpdateScheduled = false; function manageScroll() { let switchScrollTop = stepsWrapper.getBoundingClientRect().top; let windowHeight = window.innerHeight; scrolledRatio = (switchScrollTop - windowHeight / 2) / (switchScrollHeight) * -1; /* clamp scrolledRatio in between 0 and 1 */ scrolledRatio = Math.min(Math.max(scrolledRatio, 0), 1); stepsWrapper.style.setProperty('--scrolled-ratio', scrolledRatio); isUpdateScheduled = false; }; window.addEventListener('scroll', () => { if (!isUpdateScheduled) { isUpdateScheduled = true; requestAnimationFrame(manageScroll); } }); }); }); /* transform: scaleY( var(--scrolled-ratio) ); transform-origin: 50% 0; */
Scrolled Ratio setup
document.addEventListener('DOMContentLoaded', function () { let switchScrollContainers = document.querySelectorAll('.switchScrollContainer'); switchScrollContainers.forEach((switchScrollContainer) => { let switchScrollHorizontal = switchScrollContainer.querySelector('.switchScrollHorizontal'); let switchScrollParent = switchScrollContainer.parentElement; let scrolledRatio = 0; let switchScrollContainerTop, switchScrollHeight, switchScrollHorizontalWidth, widthToScroll; function updateValues() { switchScrollHeight = switchScrollParent.offsetHeight; switchScrollHorizontalWidth = switchScrollHorizontal.offsetWidth; widthToScroll = switchScrollHorizontalWidth - window.innerWidth; switchScrollContainerTop = (window.innerHeight - switchScrollContainer.offsetHeight) / 2; switchScrollContainer.style.top = `${switchScrollContainerTop}px`; } updateValues(); /* update on resize event */ window.addEventListener('resize', () => { updateValues(); }); /* Define a variable to keep track of whether the update function is already scheduled */ let isUpdateScheduled = false; function manageScroll() { let switchScrollTop = switchScrollParent.getBoundingClientRect().top; let windowHeight = window.innerHeight; /* calculate the ratio of the grid element that has been scrolled from the 2/3 of the screen */ scrolledRatio = (switchScrollTop - switchScrollContainerTop) / (switchScrollHeight - windowHeight / 2) * -1; /* clamp scrolledRatio in between 0 and 1 */ scrolledRatio = Math.min(Math.max(scrolledRatio, 0), 1); switchScrollHorizontal.style.transform = `translateX(${scrolledRatio * widthToScroll * -1}px)`; /* Reset the update scheduled flag */ isUpdateScheduled = false; }; /* Listen to the scroll event on the window object and schedule the update function using throttling */ window.addEventListener('scroll', () => { /* Only schedule the update function if it hasn't been scheduled yet */ if (!isUpdateScheduled) { /* Set the update scheduled flag */ isUpdateScheduled = true; /* Request an animation frame to update the CSS variables */ requestAnimationFrame(manageScroll); } }); }); });
freemode swipers horizontal scroll equivalent with looping possible
<script> window.addEventListener('elementor/frontend/init', function() { setTimeout(function() { let freeModeSwipers = document.querySelectorAll('.freemode .swiper-container'); freeModeSwipers.forEach(freeModeSwiper => { freeModeSwiper.swiper.params.freeMode = true; freeModeSwiper.swiper.update(); }); }, 1300); }); </script>
layout shift explanation anchor scroll
What you describes usually happens because the page you are on has image lazy loading that doesn't consider the space the images will be after being loaded. When the images then are scrolled to, and load, it creates a layout shift, and the total height of the page changes. The auto scrolling pre calculated scrollTo position becomes wrong. That's why going back up, and down again, it works fine. One way to fix this is to switch to another lazy loading provider that will better respect the size the images will be, so that problem will be avoided.
CSS Columns avoid break inside
.elementor-loop-container .e-loop-item { -webkit-column-break-inside: avoid;page-break-inside: avoid; break-inside: avoid-column; margin-top: 0 !important; }
Get computed translate X and Y value of an element
let transform = window.getComputedStyle(element).transform.split(','); let translateXPos = +transform[4].trim(); let translateYPos = +transform[5].trim().slice(0,-1);
Prevent automatic scrolling of page to anchor by browser after loading of page.
/** * Prevent automatic scrolling of page to anchor by browser after loading of page. * Do not call this function in $(...) or $(window).on('load', ...), * it should be called earlier, as soon as possible. * https://stackoverflow.com/a/39254773 */ function preventAnchorScroll() { var scrollToTop = function () { window.scrollTo(0, 0); }; if (window.location.href.indexOf('#') > -1) { window.addEventListener('scroll', function onScroll() { scrollToTop(); window.removeEventListener('scroll', onScroll); /* remove listener after one execution */ }); } // make sure to release scroll 0.9 second after document is ready // to avoid negative UX document.addEventListener('DOMContentLoaded', function () { setTimeout( function () { window.removeEventListener('scroll', scrollToTop); document.body.focus(); }, 900 ); }); } preventAnchorScroll();
ACF Repeater Within Repeater Q&A Question and Answers section in a single post template, full setup
function custom_qa_section_shortcode($atts, $content = null) { ob_start(); if(have_rows('q&a-section')): while(have_rows('q&a-section')): the_row(); echo '<div class="qa-section-group">'; echo '<h2 class="qa-section-title">' . get_sub_field('uberschrift') . '</h2>'; // Title for this group of accordions $qa = 1; if(have_rows('frageantwort')): while(have_rows('frageantwort')): the_row(); $accordion_header_id = "accordion-header-" . $qa; $accordion_panel_id = "accordion-panel-" . $qa; echo '<h3 class="qa-accordion-header">'; echo '<button id="' . $accordion_header_id . '" class="qa-accordion-btn" aria-expanded="false" aria-controls="' . $accordion_panel_id . '">'; echo '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" class="qa-accordion-icon"><path d="m982.89,520.92h-461.97v461.97h-41.84v-461.97H17.11v-41.84h461.97V17.11h41.84v461.97h461.97v41.84Z"/></svg>'; echo get_sub_field('frage'); // Question as the accordion header echo '</button>'; echo '</h3>'; echo '<section id="' . $accordion_panel_id . '" class="qa-accordion-content" aria-labelledby="' . $accordion_header_id . '" hidden>'; echo get_sub_field('antwort'); // Answer as the accordion content echo '</section>'; $qa++; endwhile; endif; echo '</div>'; endwhile; endif; return ob_get_clean(); } add_shortcode('custom_qa_section', 'custom_qa_section_shortcode'); /* qa-section-group - Represents each Q&A section. qa-section-title - Represents the title of the Q&A section. qa-accordion-header - Represents each accordion header. qa-accordion-btn - Represents the button in each accordion header. qa-accordion-icon - Represents the SVG icon inside the accordion button. qa-accordion-content - Represents the content of each accordion. qa-answer - Represents the answer inside each accordion content. */ .qa-accordion-header { border-top: 1px solid #E1E1E1; padding-top: 1.5rem; } .qa-section-group h2 { text-align: center; margin-bottom: 24px; text-transform: uppercase; } .qa-accordion-btn.qa-accordion-btn.qa-accordion-btn { font-weight: bold; position: relative; padding-left: 32px; border: none; color: #333; font-size: 1rem; background-color: transparent; white-space: initial; text-align: left; outline: none; } .qa-accordion-btn.qa-accordion-btn.qa-accordion-btn:focus-visible { outline: auto; } .qa-accordion-icon { position: absolute; left: 6px; top: 18px; fill: #3b433e; max-width: 14px; transition: .3s all ease; transform: scale(1.15); } .qa-accordion-btn:hover svg { fill: #636f65; } .qa-accordion-btn.qa-accordion-btn.qa-accordion-btn:hover { color: #636f65; } [aria-expanded="true"].qa-accordion-btn svg { transform: scale(1.15) rotate(45deg); } <script> /* question and answers JS */ document.addEventListener('DOMContentLoaded',function(){ const accordionHeaders = document.querySelectorAll('.qa-accordion-btn'); Array.prototype.forEach.call(accordionHeaders, accordionHeader => { let target = accordionHeader.parentElement.nextElementSibling; accordionHeader.onclick = () => { let expanded = accordionHeader.getAttribute('aria-expanded') === 'true' || false; accordionHeader.setAttribute('aria-expanded', !expanded); target.hidden = expanded; } }) }) </script> /* jQuery version, with animation */ <script> /* question and answers JS */ document.addEventListener('DOMContentLoaded',function(){ $ = jQuery; const accordionHeaders = document.querySelectorAll('.qa-accordion-btn'); Array.prototype.forEach.call(accordionHeaders, accordionHeader => { let target = $(accordionHeader.parentElement.nextElementSibling); accordionHeader.onclick = () => { let expanded = accordionHeader.getAttribute('aria-expanded') === 'true'; if (expanded) { target.slideUp(function() { target.attr('hidden', true); }); } else { target.removeAttr('hidden'); // Remove hidden attribute temporarily target.hide().slideDown(); // Initially hide (to reset the slide) and then slide down } accordionHeader.setAttribute('aria-expanded', !expanded); } }); }); </script>
Arrow growing on hover, CSS only with pseudo elements, works well
.elementor-cta__title.elementor-cta__content-item.elementor-content-item span::after { content: ''; width: 20px; height: 20px; border-right: 4px solid #fff; border-top: 4px solid #fff; position: absolute; transform: rotate(45deg); transition: transform 0.3s linear; } .elementor-cta__title.elementor-cta__content-item.elementor-content-item span::before { content: ''; height: 4px; width: 100px; position: absolute; top: 8px; background-color: #fff; left: -79px; transform-origin: 100% 50%; transform: scaleX(0); transition: transform 0.3s linear; } .help-card-container:hover .elementor-cta__title.elementor-cta__content-item.elementor-content-item span::before { transform: scaleX(1); } .help-card-container .elementor-cta__title.elementor-cta__content-item.elementor-content-item span { transition: transform 0.3s linear; display: block; position: absolute; left: 50%; top: calc(160% + 8px); } .help-card-container:hover .elementor-cta__title.elementor-cta__content-item.elementor-content-item span { transform: translateX(25px); }
Video Card Swiper as on CPQ website for Ferland
<script> /* Code from https://element.how/elementor-loop-carousel-premium-mods/ * Version 1.0 * Copyright 2023 Maxime Desrosiers * Licensed for personal and commercial use for customers who purchased access * No redistribution or resale allowed */ (function(){ if (document.querySelector('.elementor-editor-active')) return; let slidesToShowSettings = { desktop: 5, tablet: 5, mobile: 1.5 } let partialViewSwipers = document.querySelectorAll('.videoCardsSwiper'); partialViewSwipers.forEach(partialSwiper => { let settingsObj = JSON.parse(partialSwiper.getAttribute('data-settings')); let slidesToShowDesktop = partialSwiper.getAttribute('data-desktop-slides-to-show') || slidesToShowSettings.desktop; let slidesToShowTablet = partialSwiper.getAttribute('data-tablet-slides-to-show') || slidesToShowSettings.tablet; let slidesToShowMobile = partialSwiper.getAttribute('data-mobile-slides-to-show') || slidesToShowSettings.mobile; settingsObj.slides_to_show = slidesToShowDesktop; settingsObj.slides_to_show_tablet = slidesToShowTablet; settingsObj.slides_to_show_mobile = slidesToShowMobile; partialSwiper.setAttribute('data-settings', JSON.stringify(settingsObj)); }) }()); </script> <script> let isIOS = (/iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)) && !window.MSStream; if (isIOS) document.body.classList.add('is-ios'); window.addEventListener('load', function () { setTimeout(function () { let isMobile = window.innerWidth < 1025; let videoCardsSwiper = document.querySelector('.videoCardsSwiper .swiper, .videoCardsSwiper .swiper-container').swiper; videoCardsSwiper.on('slideChangeTransitionEnd', function () { setupVideoEvents(); if (isMobile) manageVideoPlayback(); }); function setupVideoEvents() { let videoCards = document.querySelectorAll('.videoCardsSwiper .swiper-slide'); videoCards.forEach(function (videoCard) { if (videoCard.classList.contains('event-listeners-added')) return; videoCard.classList.add('event-listeners-added'); let video = videoCard.querySelector('video'); videoCard.addEventListener('mouseenter', function () { video.play(); }); videoCard.addEventListener('click', function () { video.play(); }); videoCard.addEventListener('mouseleave', function () { video.pause(); }); }); } setupVideoEvents(); function manageVideoPlayback() { let videoCards = document.querySelectorAll('.videoCardsSwiper .swiper-slide'); let activeCard = window.innerWidth < 767 ? videoCardsSwiper.slides[videoCardsSwiper.activeIndex] : videoCardsSwiper.slides[videoCardsSwiper.activeIndex + 2]; videoCards.forEach(function (videoCard) { let video = videoCard.querySelector('video'); video.pause(); if (videoCard === activeCard) video.play(); }); } /* init on page load */ if (window.innerWidth < 1025) manageVideoPlayback(); function setVideoMuteStatus(isMuted) { let videoCards = document.querySelectorAll('.videoCardsSwiper .swiper-slide'); videoCards.forEach(function (videoCard) { let video = videoCard.querySelector('video'); video.muted = isMuted; }); } let observerOptions = { root: null, rootMargin: '0px', threshold: 0.1, /* 10% */ }; let observer = new IntersectionObserver(function (entries, observer) { entries.forEach(entry => { if (entry.isIntersecting) { setVideoMuteStatus(false); /* unmute videos */ } else { setVideoMuteStatus(true); /* mute videos */ } }); }, observerOptions); let videoCardsSwiperElement = document.querySelector('.videoCardsSwiper .swiper, .videoCardsSwiper .swiper-container'); observer.observe(videoCardsSwiperElement); }, 900); }); </script>
Swiper with Circular Progress Dots and custom transition, from alexander-ramsey.com
<style> .path_swiper .swiper-containere { width: 100%; overflow: hidden; } .path_swiper .swiper-wrapper { width: 100%; } .path_swiper .swiper-slide { width: 100%; transition: clip-path 0s cubic-bezier(0.78, 0.05, 0.21, 0.94) 0.7s; clip-path: polygon(0 0, 0 0, 0 100%, 0% 100%); } .path_swiper .swiper-slide.swiper-slide-active, .path_swiper .swiper-slide.swiper-slide-duplicate-active { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); transition: clip-path .7s cubic-bezier(0.78, 0.05, 0.21, 0.94) 0s; } .path_swiper .swiper-slide .ecs-link-wrapper { width: 100%; } .path_swiper .swiper-slide .elementor-cta__bg-wrapper { width: calc(100% + 40px); transform: translateX(-40px); transition: transform 6.9s cubic-bezier(.74,-1.01,.65,.61) 0s; } .path_swiper .swiper-slide.swiper-slide-active .elementor-cta__bg-wrapper, .path_swiper .swiper-slide.swiper-slide-duplicate-active .elementor-cta__bg-wrapper { transform: translateX(0); transition: transform 6.9s linear 0s; } .path_swiper .swiper-slide.swiper-slide-active .elementor-animated-content .elementor-animated-item--enter-from-bottom, .path_swiper .swiper-slide.swiper-slide-duplicate-active .elementor-animated-content .elementor-animated-item--enter-from-bottom{ opacity: 1; -webkit-transform: translateY(0) translateX(0); -ms-transform: translateY(0) translateX(0); transform: translateY(0) translateX(0); } .path_swiper .swiper-slide.swiper-slide-active .elementor-cta__title.elementor-animated-item--enter-from-bottom, .path_swiper .swiper-slide.swiper-slide-duplicate-active .elementor-cta__title.elementor-animated-item--enter-from-bottom{ transition-delay:.9s; } .path_swiper .swiper-slide.swiper-slide-active .elementor-cta__description.elementor-animated-item--enter-from-bottom, .path_swiper .swiper-slide.swiper-slide-duplicate-active .elementor-cta__description.elementor-animated-item--enter-from-bottom{ transition-delay:1.2s !important; } .path_swiper .swiper-slide.swiper-slide-active .elementor-cta__button-wrapper.elementor-animated-item--enter-from-bottom, .path_swiper .swiper-slide.swiper-slide-duplicate-active .elementor-cta__button-wrapper.elementor-animated-item--enter-from-bottom { transition-delay:1.5s !important; } .path_swiper .swiper-slide.swiper-slide-active .elementor-cta .elementor-cta__title, .path_swiper .swiper-slide.swiper-slide-active .elementor-cta .elementor-cta__description, .path_swiper .swiper-slide.swiper-slide-duplicate-active .elementor-cta .elementor-cta__title, .path_swiper .swiper-slide.swiper-slide-duplicate-active .elementor-cta .elementor-cta__description, { color: #FFF; } .path_swiper .swiper-slide.swiper-slide-active .dark-text .elementor-cta .elementor-cta__title, .path_swiper .swiper-slide.swiper-slide-active .dark-text .elementor-cta .elementor-cta__description, .path_swiper .swiper-slide.swiper-slide-duplicate-active .dark-text .elementor-cta .elementor-cta__title, .path_swiper .swiper-slide.swiper-slide-duplicate-active .dark-text .elementor-cta .elementor-cta__description { color: #333; } .path_swiper .progress-circle { transform: rotate(-90deg); } .path_swiper .progress-circle-back { fill: none; stroke: #333; stroke-width:4px; opacity: 0.3; } .path_swiper .progress-circle-prog { fill: none; stroke: #333; stroke-width: 4px; stroke-dasharray: 0 999; stroke-dashoffset: 0px; transition: stroke-dasharray 0s linear 0s; } .path_swiper .swiper-pagination-bullet.swiper-pagination-bullet-active .progress-circle-prog { stroke-dasharray: 100 0; transition: stroke-dasharray 10.9s linear 0s; } .path_swiper .swiper-paginatione.swiper-pagination-clickable.swiper-pagination-bullets.swiper-pagination-horizontal { position: absolute; top: 5%; right: 5%; z-index: 999; } .path_swiper .swiper-pagination-bullet { margin:0; width: 32px; height: 34px; border-radius: initial; opacity: 1; background: transparent; } span.swiper-notification { display: none; } @media (max-width:767px){ .path_swiper .swiper-paginatione.swiper-pagination-clickable.swiper-pagination-bullets.swiper-pagination-horizontal { top: unset; bottom:3%; } } </style> <script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/8.0.3/swiper-bundle.min.js" integrity="sha512-+1MtN0rwSlEnZo0agG99mCQChlBtfe8hyHItwWtgiD33mWvaNVZkZB9yX2S92z5HgDg1+EcitjutZejbHUcZAg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script> document.addEventListener('DOMContentLoaded', function() { (function() { let parents = document.querySelectorAll('.path_swiper .ecs-posts'); parents.forEach((parent, i, arr) => { let children = parent.querySelectorAll('article'); let wrapper = document.createElement('div'); wrapper.className = 'swiper-wrapper'; children.forEach((childe) => { wrapper.appendChild(childe); childe.classList.add('swiper-slide'); }); let container = document.createElement('div'); container.className = 'swiper-containere'; let pagination = document.createElement('div'); pagination.className = 'swiper-paginatione'; container.append(pagination); container.prepend(wrapper); parent.prepend(container); var swiper = new Swiper(container, { effect: "creative", creativeEffect: { prev: { translate: [0, 0, "1px"], /* Needs atleast 1px otherwise autoplay bugs and stops */ }, }, speed: 700, loop: true, pagination: { el: '.swiper-paginatione', clickable: true, renderBullet: function (index, className) { return `<svg class="progress-circle ${className}" width="40px" height="40px" xmlns="http://www.w3.org/2000/svg"> <circle class="progress-circle-back" cx="18" cy="18" r="10"></circle> <circle class="progress-circle-prog" cx="18" cy="18" r="10"></circle> </svg>`; }, }, autoplay: { delay: 6000, disableOnInteraction: false, }, on: { init: function() { let startFirstPaginationTransition = container.querySelector('.swiper-pagination-bullet-active'); startFirstPaginationTransition.classList.remove('swiper-pagination-bullet-active'); setTimeout(function(){ startFirstPaginationTransition.classList.add('swiper-pagination-bullet-active'); },114); }, }, }); }); })(); }); </script>
WooCommerce Shortcode & AJAX code to create an add to cart that will have the variations, in a list or table format, for quickly adding to cart many products
function enqueue_custom_js() { if (is_page(54920)) { ob_start(); ?> <script> document.addEventListener('DOMContentLoaded', function () { var $ = jQuery; $('body').on('submit', 'form.cart', function (e) { e.preventDefault(); var $form = $(this), product_id = $form.find('[name=add-to-cart]').val() || $form.data('product_id'), quantity = $form.find('input[name=quantity]').val(), product_variations = $form.data('product_variations'), selected_variation_id = product_id; // Default to the product_id for single products var selected_attributes = {}; // Dynamically find all <select> elements within the form $form.find('select').each(function () { var $this = $(this); var attributeName = $this.attr('name').replace('attribute_', ''); // remove 'attribute_' prefix selected_attributes[attributeName] = $this.val(); }); for (var i = 0; i < product_variations.length; i++) { var variation = product_variations[i]; var isMatch = true; // Compare attributes for (var attributeName in selected_attributes) { if (selected_attributes.hasOwnProperty(attributeName)) { if (variation.attributes['attribute_' + attributeName] !== selected_attributes[attributeName]) { isMatch = false; break; } } } if (isMatch) { selected_variation_id = variation.variation_id; break; } } var data = { product_id: selected_variation_id, quantity: quantity }; $.ajax({ type: 'POST', url: wc_add_to_cart_params.wc_ajax_url.toString().replace('%%endpoint%%', 'add_to_cart'), data: data, success: function (response) { if (response.error & response.product_url) { window.location = response.product_url; return; } else { $(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash, $form]); } }, }); }); }); </script> <?php echo ob_get_clean(); } } add_action('wp_footer', 'enqueue_custom_js'); function ajax_add_to_cart_button_with_variations_shortcode($atts) { global $product; if (!$product) { return ''; } ob_start(); if ($product->is_type('variable')) { ?> <form class="variations_form cart" method="post" enctype='multipart/form-data' data-product_id="<?php echo absint($product->get_id()); ?>" data-product_variations="<?php echo htmlspecialchars(wp_json_encode($product->get_available_variations())) ?>"> <?php echo wc_get_stock_html($product); ?> <table class="variations"> <tbody> <?php foreach ($product->get_variation_attributes() as $attribute_name => $options) : ?> <tr> <td> <label for="<?php echo sanitize_title($attribute_name); ?>"><?php echo wc_attribute_label($attribute_name); ?></label> </td> <td> <?php wc_dropdown_variation_attribute_options(array( 'options' => $options, 'attribute' => $attribute_name, 'product' => $product, )); ?> </td> </tr> <?php endforeach; ?> </tbody> </table> <div class="single_variation_wrap"> <div class="woocommerce-variation single_variation"></div> <div class="woocommerce-variation-add-to-cart variations_button"> <?php do_action('woocommerce_before_add_to_cart_button'); ?> <div class="quantity"><input type="number" name="quantity" value="1" title="Qty" class="input-text qty text" size="4" pattern="[0-9]*" inputmode="numeric" /></div> <button type="submit" class="single_add_to_cart_button button alt"><?php echo esc_html($product->single_add_to_cart_text()); ?></button> <?php do_action('woocommerce_after_add_to_cart_button'); ?> </div> </div> </form> <?php } else { ?> <form class="cart" method="post" enctype='multipart/form-data'> <?php do_action('woocommerce_before_add_to_cart_button'); ?> <div class="quantity"><input type="number" name="quantity" value="1" title="Qty" class="input-text qty text" size="4" pattern="[0-9]*" inputmode="numeric" /></div> <button type="submit" name="add-to-cart" value="<?php echo esc_attr($product->get_id()); ?>" class="single_add_to_cart_button button alt"><?php echo esc_html($product->single_add_to_cart_text()); ?></button> <?php do_action('woocommerce_after_add_to_cart_button'); ?> </form> <?php } return ob_get_clean(); } add_shortcode('ajax_add_to_cart_button_with_variations', 'ajax_add_to_cart_button_with_variations_shortcode');
Simple Content Switcher Vanilla JS
<script> document.addEventListener('DOMContentLoaded',function(){ let switchers = document.querySelectorAll('.listSwitcher li a'); let switchedContent = document.querySelectorAll('.switchedContent'); switchers.forEach((switcher,i) => { switcher.addEventListener('click', function(e) { e.preventDefault(); switchers.forEach((switcher) => { switcher.classList.remove('is-active'); }); switcher.classList.add('is-active'); switchedContent.forEach((content) => { content.classList.remove('is-active'); }); switchedContent[i].classList.add('is-active'); }); }); /* make the first switcher and switched content active on page load */ switchers[0].classList.add('is-active'); switchedContent[0].classList.add('is-active'); /* Allow role=button to behave like a real <button> el. by triggering the click on spacebar/enter keydown */ document.querySelectorAll('.listSwitcher a[role="button"]').forEach(el => { el.addEventListener('keydown', e => { const keyDown = e.key !== undefined ? e.key : e.keyCode; if ( (keyDown === 'Enter' || keyDown === 13) || (['Spacebar', ' '].indexOf(keyDown) >= 0 || keyDown === 32)) { /* prevent default so the page doesn't scroll when pressing space */ e.preventDefault(); el.click(); } }); }); }); </script> <style> /* left margin on the switchers */ .listSwitcher { margin-left: 14%; } /* increment styling (01 02 03 04) */ .listSwitcher .elementor-icon-list-text span { color: #86A9BC; opacity: 0.5; font-size: 12px; margin-top: -4px; margin-right: 15px; display: inline-block; font-family: "SuisseIntl-SemiBold"; font-weight: 600; } /* display flex for better alignment of the increment and text */ .listSwitcher .elementor-icon-list-text { display: flex; } /* when is active, make the text color white */ .listSwitcher.listSwitcher.listSwitcher.listSwitcher .is-active .elementor-icon-list-text{ color: #ffffff; } /* don't show the switchedContent, except in the Elementor editor */ body:not(.elementor-editor-active) .switchedContent { display: none; } /* show the switchedContent when is active */ body:not(.elementor-editor-active) .switchedContent.is-active { display: flex; } /* the styling for the 1 2 3 4 */ .switchedContentHeading.switchedContentHeading.switchedContentHeading { margin-bottom: 50px; /* spacing below the 1 2 3 4 */ animation: fadeDown 0.4s linear forwards; } /* styles for the text that overlays the 1 2 3 4 */ .switchedContentTitle { position: absolute; font-size: 1.875rem; width: 100%; left: 50%; top:50%; transform: translate(-50%,-50%) ; line-height: 2rem; color: #ffffff; } /* the text editor below the 1 2 3 4 */ .switchedContentText { opacity: 0; animation: fadeDown 0.4s linear forwards 0.3s; } /* rest of the styles are directly through the Elementor UI */ </style>
Range Slider And Input Field Combo Field
<fieldset class="range-field-combo-wrapper"> <div class="range-slider flat" style='--min:0; --max:4000; --step:1; --value:2500; --text-value:"2500";'> <input type="range" min="0" max="4000" value="2500" oninput="this.parentNode.style.setProperty('--value',this.value); this.parentNode.style.setProperty('--text-value', JSON.stringify(this.value))"> <output></output> <div class='range-slider__progress'></div> </div> <div class='valueInputWrapper'> <input class="inputNumber" type="number" min="0" max="4000" value="2500"/> </div> </fieldset> <style> /* range slider css */ .range-slider.flat { --thumb-size: 23px; --track-height: 10px; --progress-shadow: none; --progress-flll-shadow: none; --thumb-shadow: 0 0 0 6px white inset, 0 0 0 99px var(--primary-color) inset; --thumb-shadow-hover: 0 0 0 7px white inset, 0 0 0 99px var(--primary-color) inset; --thumb-shadow-active: 0 0 0 7px white inset, 0 0 0 99px var(--primary-color) inset; } .range-slider { --primary-color: #577DF7; --value-offset-y: var(--ticks-gap); --value-active-color: #171437; --value-background: #FFFFFF; --value-background-hover: #FFFFFF; --value-font: 700 12px/1 inherit; --fill-color: var(--primary-color); --progress-background: #C9C9C9; --progress-radius: 20px; --track-height: calc(var(--thumb-size) / 2); --min-max-font: 12px inherit; --min-max-opacity: 0.5; --min-max-x-offset: 10%; --thumb-size: 22px; --thumb-color: white; --thumb-shadow: 0 0 3px rgba(0, 0, 0, 0.4), 0 0 1px rgba(0, 0, 0, 0.5) inset, 0 0 0 99px var(--thumb-color) inset; --thumb-shadow-active: 0 0 0 calc(var(--thumb-size) / 4) inset var(--thumb-color), 0 0 0 99px var(--primary-color) inset, 0 0 3px rgba(0, 0, 0, 0.4); --thumb-shadow-hover: var(--thumb-shadow); --ticks-thickness: 0px; --ticks-height: 5px; --ticks-gap: var( --ticks-height, 0 ); --ticks-color: silver; --step: 1; --value-a: Clamp( var(--min), var(--value, 0), var(--max) ); --value-b: var(--value, 0); --text-value-a: var(--text-value, ""); --completed-a: calc( (var(--value-a) - var(--min)) / (var(--max) - var(--min)) * 100 ); --completed-b: calc( (var(--value-b) - var(--min)) / (var(--max) - var(--min)) * 100 ); --ca: Min(var(--completed-a), var(--completed-b)); --cb: Max(var(--completed-a), var(--completed-b)); --thumbs-too-close: Clamp( -1, 1000 * (Min(1, Max(var(--cb) - var(--ca) - 5, -1)) + 0.001), 1 ); --thumb-close-to-min: Min(1, Max(var(--ca) - 2, 0)); --thumb-close-to-max: Min(1, Max(98 - var(--cb), 0)); display: inline-block; height: max(var(--track-height), var(--thumb-size)); padding-bottom: var(--flip-y, var(--ticks-gap)); padding-top: calc(var(--flip-y) * var(--ticks-gap)); position: relative; z-index: 1; } .range-slider__progress { --start-end: calc(var(--thumb-size) / 2); --clip-end: calc(100% - (var(--cb)) * 1%); --clip-start: calc(var(--ca) * 1%); --clip: inset(-20px var(--clip-end) -20px var(--clip-start)); position: absolute; left: 0; right: var(--start-end); top: calc( var(--ticks-gap) * var(--flip-y, 0) + var(--thumb-size) / 2 - var(--track-height) / 2 ); height: calc(var(--track-height)); background: var(--progress-background, #eee); pointer-events: none; z-index: -1; border-radius: var(--progress-radius); outline: solid #F3F3F3 1px; } .range-slider__progress::before { content: ""; position: absolute; left: 0; right: 0; -webkit-clip-path: var(--clip); clip-path: var(--clip); top: 0; bottom: 0; background: var(--fill-color, black); box-shadow: var(--progress-flll-shadow); z-index: 1; border-radius: inherit; } .range-slider__progress::after { content: ""; position: absolute; top: 0; right: 0; bottom: 0; left: 0; box-shadow: var(--progress-shadow); pointer-events: none; border-radius: inherit; } .range-slider > input { -webkit-appearance: none; width: 100%; height: var(--thumb-size); margin: 0; position: absolute; left: 0; top: calc( 50% - Max(var(--track-height), var(--thumb-size)) / 2 + calc(var(--ticks-gap) / 2 * var(--flip-y, -1)) ); cursor: -webkit-grab; cursor: grab; outline: none; background: none; } .range-slider > input::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; height: var(--thumb-size); width: var(--thumb-size); transform: var(--thumb-transform); border-radius: var(--thumb-radius, 50%); background: var(--thumb-color); box-shadow: var(--thumb-shadow); border: none; pointer-events: auto; -webkit-transition: 0.1s; transition: 0.1s; } .range-slider > input::-moz-range-thumb { -moz-appearance: none; appearance: none; height: var(--thumb-size); width: var(--thumb-size); transform: var(--thumb-transform); border-radius: var(--thumb-radius, 50%); background: var(--thumb-color); box-shadow: var(--thumb-shadow); border: none; pointer-events: auto; -moz-transition: 0.1s; transition: 0.1s; } .range-slider > input::-ms-thumb { appearance: none; height: var(--thumb-size); width: var(--thumb-size); transform: var(--thumb-transform); border-radius: var(--thumb-radius, 50%); background: var(--thumb-color); box-shadow: var(--thumb-shadow); border: none; pointer-events: auto; -ms-transition: 0.1s; transition: 0.1s; } .range-slider > input:hover { --thumb-shadow: var(--thumb-shadow-hover); } .range-slider > input:hover + output { --value-background: var(--value-background-hover); --y-offset: -5px; color: var(--value-active-color); box-shadow: 0 0 0 3px var(--value-background); } .range-slider > input:active { --thumb-shadow: var(--thumb-shadow-active); cursor: -webkit-grabbing; cursor: grabbing; z-index: 2; } .range-slider > input:active + output { transition: 0s; } .range-slider > input:nth-of-type(1) { --is-left-most: Clamp(0, (var(--value-a) - var(--value-b)) * 99999, 1); } .range-slider > input:nth-of-type(1) + output:not(:only-of-type) { --flip: calc(var(--thumbs-too-close) * -1); } .range-slider > input:nth-of-type(1) + output::after { content: var(--prefix, "") var(--text-value-a) var(--suffix, ""); border: solid 1px #E1E1E1; padding:4px 6px; background-color:#fff; } .range-slider > input:nth-of-type(2) { --is-left-most: Clamp(0, (var(--value-b) - var(--value-a)) * 99999, 1); } .range-slider > input:nth-of-type(2) + output { --value: var(--value-b); } .range-slider > input:only-of-type ~ .range-slider__progress { --clip-start: 0; } .range-slider > input + output { --flip: 1; --x-offset: calc(var(--completed-b) * -1%); --pos: calc( ((var(--value) - var(--min)) / (var(--max) - var(--min))) * 100% ); pointer-events: none; position: absolute; z-index: 5; background: var(--value-background); border-radius: 10px; padding: 2px 4px; left: var(--pos); transform: translate(var(--x-offset), calc( 150% * var(--flip) - (var(--y-offset, 0px) + var(--value-offset-y)) * var(--flip) )); transition: all 0.12s ease-out, left 0s; } .range-slider > input + output::after { content: var(--prefix, "") var(--text-value-b) var(--suffix, ""); font: var(--value-font); } .range-field-combo-wrapper { display:flex; align-items:flex-start; border:0; padding: 0 0 10px; } .range-field-combo-wrapper .range-slider{ flex-grow:1; } .valueInputWrapper { margin-left:12px; position:relative; } .valueInputWrapper > input { color:#577DF7; border: 1px solid #C9C9C9; border-radius: 16px; padding: 12px 20px; width:110px; font-size:1.2rem; } .valueInputWrapper::after, .valueInputWrapper::before { content:var(--suffix,""); color:#577DF7; font-size:1.2rem; position:absolute; top:50%; transform:translateY(-50%); left:calc(20px + 1.5rem); } .valueInputWrapper::before { content:var(--prefix,""); left:calc(20px - .7rem); } </style> <script> document.addEventListener('DOMContentLoaded', function () { let rangeFieldWrappers = document.querySelectorAll('.range-field-combo-wrapper'); let realInputsIDArray = ['input_1_1', 'input_1_3', 'input_1_4', 'input_1_8']; let realInputsElementsArray = realInputsIDArray.map(id => document.getElementById(id)); rangeFieldWrappers.forEach((wrapper, i) => { let range = wrapper.querySelector('.range-slider input'); let field = wrapper.querySelector('.valueInputWrapper input'); range.addEventListener('input', function (e) { field.value = e.target.value; realInputsElementsArray[i].value = e.target.value; }); field.addEventListener('input', function (e) { if (e.target.value !== '') { range.value = e.target.value; range.dispatchEvent(new Event('input')); } realInputsElementsArray[i].value = e.target.value; }); }); /* currency updater, gravity form field ID 1_16 */ /* elements with --prefix that's a currency need the data attribute data-has-currency */ let currencyRealInput = document.getElementById('input_1_16'); let currencyFakeInput = document.getElementById('currency-select'); currencyFakeInput.addEventListener('change', function (e) { currencyRealInput.value = e.target.value; updateCurrencies(); }); function updateCurrencies() { document.querySelectorAll('[data-has-currency]').forEach(input => { input.style.setProperty('--prefix', `"${currencyFakeInput.value}"`); }); } /* END OF currency updater */ let fakeSubmitButton = document.querySelector('#fake-submit-button'); let realSubmitButton = document.querySelector('#gform_submit_button_1'); fakeSubmitButton.addEventListener('click', function (e) { e.preventDefault(); realSubmitButton.click(); }); }); /* to get the sliders be properly formatted when clicking back button window.addEventListener('pageshow', function (event) { let rangeFieldWrappers = document.querySelectorAll('.range-field-combo-wrapper'); rangeFieldWrappers.forEach((wrapper, i) => { let range = wrapper.querySelector('.range-slider input'); range.dispatchEvent(new Event('input')); }); }); */ /* Check if the first input field has a value on clicking back button, if so refresh page */ window.addEventListener('pageshow', function (event) { let sectionWrapper = document.querySelector('.sectionWrapper'); let inputs = Array.from(sectionWrapper.querySelectorAll('input, select')).filter(function (input) { return input.type !== 'hidden' && input.type !== 'submit'; }); inputs.forEach(function (input, index) { let urlSlug = window.location.pathname.split('/').pop(); let key = urlSlug + '_' + index; let storedValue = sessionStorage.getItem(key); if (storedValue) { console.log(storedValue); } }); let firstInput = inputs[0]; if (firstInput.value !== '') { // Store input values in sessionStorage inputs.forEach(function (input, index) { let urlSlug = window.location.pathname.split('/').pop(); let key = urlSlug + '_' + index; sessionStorage.setItem(key, input.value); }); window.location.href = window.location.href; } inputs.forEach(function (input, index) { let urlSlug = window.location.pathname.split('/').pop(); let key = urlSlug + '_' + index; let storedValue = sessionStorage.getItem(key); if (storedValue) { input.value = storedValue; } }); /* Trigger an event to ensure that sliders or other dynamic elements are properly updated */ let rangeFieldWrappers = document.querySelectorAll('.range-field-combo-wrapper'); rangeFieldWrappers.forEach((wrapper, i) => { let range = wrapper.querySelector('.range-slider input'); range.dispatchEvent(new Event('input')); }); let selects = document.querySelectorAll('select'); selects.forEach(select => { select.dispatchEvent(new Event('change')); }); }); </script>
Add Circular Circle Progress pagination to any swiper
window.addEventListener('load', function () { let svgCircles = `<svg class="progress-circle" width="40px" height="40px" xmlns="http://www.w3.org/2000/svg"> <circle class="progress-circle-back" cx="20" cy="20" r="20"></circle> <circle class="progress-circle-prog" cx="20" cy="20" r="20"></circle> </svg>`; let swiperProgresses = document.querySelectorAll('.circle-progress-swiper .swiper-pagination-bullet'); swiperProgresses.forEach((progress, index) => { progress.innerHTML = svgCircles; }); swiperProgresses[0].classList.remove('swiper-pagination-bullet-active'); setTimeout(function () { swiperProgresses[0].classList.add('swiper-pagination-bullet-active'); }, 74); }); /* .circle-progress-swiper .swiper-horizontal { counter-reset: slide; } .circle-progress-swiper .swiper-horizontal.swiper-horizontal.swiper-horizontal .swiper-pagination-bullet { background-color: transparent; width: 40px; height: 40px; margin: 0 10px; position: relative; counter-increment: slide; } .circle-progress-swiper .swiper-horizontal.swiper-horizontal.swiper-horizontal .swiper-pagination-bullet::before { content: counter(slide); position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); font-size: 1rem; } .circle-progress-swiper .swiper-horizontal .swiper-pagination.swiper-pagination.swiper-pagination.swiper-pagination { width: fit-content; left: max(calc((100% - 1400px) / 2), 20px); bottom: 100px; } .circle-progress-swiper .progress-circle { transform: rotate(-90deg); overflow: visible; } .circle-progress-swiper .progress-circle-back { fill: none; stroke: #FFF; stroke-width: 2px; opacity: 0.3; } .circle-progress-swiper .progress-circle-prog { fill: none; stroke: #FFF; stroke-width: 2px; stroke-dasharray: 0 999; stroke-dashoffset: 0px; transition: stroke-dasharray 0s linear 0s; } .circle-progress-swiper .swiper-pagination-bullet.swiper-pagination-bullet-active .progress-circle-prog { stroke-dasharray: 130 0; transition: stroke-dasharray 5s linear 0s; } */
Window resize width event listener, performance optimized
let currentWidth = window.innerWidth; let resizeTO; window.addEventListener('resize', function () { if (window.innerWidth === currentWidth) return; currentWidth = window.innerWidth; clearTimeout(resizeTO); resizeTO = setTimeout(function () { fallbackFlexGap(); }, 700); });
Init new swiper with containers only, parent container = .swiper-container, then a container .swiper-wrapper, then containers or elements with .swiper-slide
let _swipere; let int = setInterval(() => { try { if (window.Swiper) { clearInterval(int); _swipere = init(); } } catch (err) { console.log(err); } }, 400); function init() { if (document.querySelector('.elementor-editor-active')) { return; } let swipere = new Swiper('.e-con.swiper-container', { loop: true, slidesPerView: 1, spaceBetween: 0, }); return swipere; }; function _swiperSlideforward() { _swipere.slideNext(); }
Setup GSAP Smooth Scroll Support Wrappers in Elementor Hello theme
function setupSmoothWrappers() { ?> <script> (function () { let bodyChildren = Array.from(document.body.childNodes); let wrapper = document.createElement('div'); wrapper.id = 'page-wrapper'; let innerWrapper = document.createElement('div'); innerWrapper.id = 'smooth-wrapper'; let pageContent = document.createElement('div'); pageContent.id = 'smooth-content'; let selectors = ['.elementor-location-header']; /* if header is sticky */ let elementsToMove = []; let nonMatchingElements = []; for (let i = 0; i < bodyChildren.length; i++) { let node = bodyChildren[i]; if (node.nodeType === 1 && node.tagName.toLowerCase() !== 'script' && node.tagName.toLowerCase() !== 'style') { let matchesSelector = selectors.some(selector => node.matches(selector)); if (matchesSelector) { elementsToMove.push(node); } else { nonMatchingElements.push(node); } } } nonMatchingElements.forEach(function (element) { pageContent.appendChild(element.cloneNode(true)); element.parentNode.removeChild(element); }); elementsToMove.forEach(function (element) { wrapper.appendChild(element.cloneNode(true)); element.parentNode.removeChild(element); }); innerWrapper.appendChild(pageContent); wrapper.appendChild(innerWrapper); document.body.insertBefore(wrapper, document.body.firstChild); })(); </script> <?php } add_action('wp_footer', 'setupSmoothWrappers');
Good looking fancy accordion design without SVG or font icon
.accordionExcelDesign .elementor-accordion { counter-reset: item; } .accordionExcelDesign .elementor-tab-title { position: relative; display:flex; flex-direction:row-reverse; justify-content:space-between; align-items:center; } .accordionExcelDesign .elementor-tab-title.elementor-tab-title a { color: var(--e-global-color-primary ); } .accordionExcelDesign .elementor-tab-title.elementor-tab-title:hover a { color: var(--e-global-color-24ff9f0 ); } .accordionExcelDesign .elementor-tab-title::after { counter-increment: item; content:counter(item, decimal-leading-zero); position: absolute; left:14px; font-size:0.8rem; top:40%; color: #B4BDC3; transition: color 0.4s; } .accordionExcelDesign .elementor-tab-title:hover::after, .accordionExcelDesign .elementor-tab-title.elementor-active::after { color: #293F5D; } .accordionExcelDesign .elementor-tab-title::before { content:''; background-color:#f3f6f7; position:absolute; width: 100%; height: 100%; left:0; top:0; transition: transform 0.4s; transform:scaleY(0); transform-origin: 0 0; } .accordionExcelDesign .elementor-tab-title:hover::before { transform:scaleY(1); transform-origin: 0 100%; } .accordionExcelDesign .elementor-tab-title a { z-index:1; } .accordionExcelDesign .elementor-tab-title.elementor-active::before { background-color:#FFFFFF; } .accordionExcelDesign .elementor-accordion-icon.elementor-accordion-icon.elementor-accordion-icon-right { background-color:var(--e-global-color-secondary); border-radius:50%; width:50px; height:50px; position:relative; transition: background-color 0.4s; } .accordionExcelDesign .elementor-tab-title:hover .elementor-accordion-icon.elementor-accordion-icon.elementor-accordion-icon-right { background-color:#FFFFFF; } .accordionExcelDesign .elementor-tab-title.elementor-active .elementor-accordion-icon.elementor-accordion-icon.elementor-accordion-icon-right { background-color:var(--e-global-color-secondary); } .accordionExcelDesign .elementor-accordion-icon::before, .elementor-accordion-icon::after { content:''; position:absolute; top:50%; left: 50%; transform: translate(-50%, -50%); height:2px; background-color:#FFFFFF; width:20px; display: block; transition: background-color 0.4s, transform 0.4s; } .accordionExcelDesign .elementor-accordion-icon::after { transform: translate(-50%, -50%) rotate(90deg); } .accordionExcelDesign .elementor-tab-title.elementor-active .elementor-accordion-icon::after { transform: translate(-50%, -50%) rotate(90deg) scaleY(0); } .accordionExcelDesign .elementor-tab-title:hover .elementor-accordion-icon::before, .accordionExcelDesign .elementor-tab-title:hover .elementor-accordion-icon::after { background-color:var(--e-global-color-secondary); } .accordionExcelDesign .elementor-tab-title.elementor-active .elementor-accordion-icon::before, .accordionExcelDesign .elementor-tab-title.elementor-active .elementor-accordion-icon::after { background-color:#FFFFFF; } .accordionExcelDesign .elementor-accordion-icon-closed.elementor-accordion-icon-closed.elementor-accordion-icon-closed { display: none; }
live calculators results page on the Retail Insight Project (Dom)
document.addEventListener('DOMContentLoaded', function () { // create a URLSearchParams object from the current search string let urlParams = new URLSearchParams(window.location.search); // get the company name and currency from the URL parameters let retailerName = urlParams.get('retailer_name'); let currency = urlParams.get('currency'); let annualOsaSolutionFee = urlParams.get('annual_osa_solution_feed'); let rangeFieldWrappers = document.querySelectorAll('.range-field-combo-wrapper'); let shownValuesClasses = ['data-annual-sales', 'data-gross-profit', 'data-current-osa', 'data-substitution', 'data-osa-improvement']; let shownValuesElementsArray = shownValuesClasses.map(className => document.querySelector(`.${className} .elementor-heading-title`)); let valuesPrefixes = []; let valuesSuffixes = []; let initalValuesArray = []; let names = ['annual_sales', 'average_gp_margin', 'current_osa', 'substitution', 'improving_to_osa']; /* names of the fields in the form, in their respective order */ /* create object with the empty values from fields in the form */ let updatedValuesObj = names.reduce((acc, name) => { acc[name] = ''; return acc; }, {}); shownValuesElementsArray.forEach((element, i) => { let value = extractValue(element.innerHTML, 'numeric'); let prefix = extractValue(element.innerHTML, 'prefix'); let suffix = extractValue(element.innerHTML, 'suffix'); initalValuesArray.push(value); updatedValuesObj[names[i]] = value; valuesPrefixes.push(prefix); valuesSuffixes.push(suffix); if (i == 0) { updatedValuesObj[names[i]] = '' + parseValue(element.innerHTML); } }); rangeFieldWrappers.forEach((wrapper, i) => { let range = wrapper.querySelector('.range-slider input'); let field = wrapper.querySelector('.valueInputWrapper input'); range.value = initalValuesArray[i]; field.value = initalValuesArray[i]; if (i == 0) { range.value = updatedValuesObj[names[i]]; field.value = updatedValuesObj[names[i]]; } range.addEventListener('input', function (e) { field.value = e.target.value; onValueChange(e, i); }); field.addEventListener('input', function (e) { if (e.target.value !== '') { range.value = e.target.value; range.dispatchEvent(new Event('input')); } onValueChange(e, i); }); }); function onValueChange(e, i) { shownValuesElementsArray[i].innerHTML = `${valuesPrefixes[i]}${formatValueWithCommas(e.target.value)}${valuesSuffixes[i]}`; if (i === 0) shownValuesElementsArray[i].innerHTML = currency + formatValue(e.target.value); updatedValuesObj[names[i]] = e.target.value; calculateValues(); updateUrlParams(); } let targetLostSaleResult, currentLostSaleResult, incrementalSalesResult, increaseGrossProfitResult, ROIResult; let annualReductionLostSalesCalculatedFields = document.querySelectorAll('.data-annual-reduction'); let increaseGrossProfitCalculatedFields = document.querySelectorAll('.data-increase-gross-profit'); let ROICalculatedFields = document.querySelectorAll('.data-roi'); function calculateValues() { targetLostSaleResult = (100 - (+updatedValuesObj["current_osa"] + +updatedValuesObj["improving_to_osa"])) / 100 * updatedValuesObj["annual_sales"] * ((100 - updatedValuesObj["substitution"]) / 100); currentLostSaleResult = ((100 - updatedValuesObj["current_osa"]) / 100) * updatedValuesObj["annual_sales"] * ((100 - updatedValuesObj["substitution"]) / 100); annualReductionLostSalesCalculatedFields.forEach(field => { field.innerHTML = currency + formatValue(currentLostSaleResult); }); incrementalSalesResult = currentLostSaleResult - targetLostSaleResult; increaseGrossProfitResult = incrementalSalesResult * (updatedValuesObj["average_gp_margin"] / 100); increaseGrossProfitCalculatedFields.forEach(field => { field.innerHTML = currency + formatValue(increaseGrossProfitResult); }); ROIResult = (increaseGrossProfitResult / 540000) * 100 - 100; ROICalculatedFields.forEach(field => { field.innerHTML = ROIResult.toFixed(0) + '%'; }); } function updateUrlParams() { // create a URLSearchParams object from the current search string let params = new URLSearchParams(window.location.search); // set each param to its corresponding form value params.set('retailer_name', retailerName); params.set('annual_sales', currency + formatValue(updatedValuesObj["annual_sales"])); params.set('average_gp_margin', updatedValuesObj["average_gp_margin"]); params.set('current_osa', updatedValuesObj["current_osa"]); params.set('substitution', updatedValuesObj["substitution"]); params.set('improving_to_osa', updatedValuesObj["improving_to_osa"]); params.set('target_lost_sale', currency + targetLostSaleResult); params.set('current_lost_sale', currency + formatValue(currentLostSaleResult)); params.set('incremental_sales', currency + formatValue(incrementalSalesResult)); params.set('increase_in_gross_profits', currency + formatValue(increaseGrossProfitResult)); params.set('annual_osa_solution_feed', annualOsaSolutionFee); params.set('roi_based_on_avg_cost_retail_insight', ROIResult.toFixed(0)); params.set('currency', currency); // create a new URL let newUrl = new URL(window.location.protocol + "//" + window.location.host + window.location.pathname); // set the search params of the new URL newUrl.search = params.toString(); // update the window's URL without reloading the page or creating a history entry window.history.replaceState({}, '', newUrl); } }); /* to get the sliders be properly formatted when clicking back button */ window.addEventListener('pageshow', function (event) { let rangeFieldWrappers = document.querySelectorAll('.range-field-combo-wrapper'); rangeFieldWrappers.forEach((wrapper, i) => { let range = wrapper.querySelector('.range-slider input'); range.dispatchEvent(new Event('input')); }); }); function extractValue(str, type) { let value; switch (type) { case 'prefix': // Extract prefix using regex const prefixMatch = str.match(/^([^\d]+)/); value = prefixMatch ? prefixMatch[0] : ''; break; case 'suffix': // Extract suffix using regex const suffixMatch = str.match(/([^\d]+)$/); value = suffixMatch ? suffixMatch[0] : ''; break; case 'numeric': // Remove commas from the string const cleanedStr = str.replace(/,/g, ''); // Extract numeric value using regex const numericMatch = cleanedStr.match(/(\d+(\.\d+)?)/); value = numericMatch ? parseFloat(numericMatch[0]) : NaN; break; default: value = NaN; } return value; } function formatValue(rawValue) { if (rawValue > 999) { value = (rawValue / 1000).toFixed(1) + 'k'; } else { value = rawValue; } return value; } /* opposite of formatValue */ function parseValue(formattedValue) { /* ignore first character if it's a currency symbol */ if (formattedValue[0] === '£' || formattedValue[0] === '$' || formattedValue[0] === '€') { formattedValue = formattedValue.slice(1); } var numericValue = 0; if (formattedValue.endsWith('k')) { numericValue = parseFloat(formattedValue) * 1000; } else { numericValue = parseFloat(formattedValue); } return numericValue; } function formatValueWithCommas(value) { value = +value; // Convert the input string to a number if (typeof value !== 'number' || isNaN(value)) { return value; // Return the input as is } // Convert the value to a string let valueStr = value.toString(); // Split the string into integer and decimal parts (if present) const parts = valueStr.split('.'); let integerPart = parts[0]; const decimalPart = parts[1] || ''; // Add commas to the integer part let formattedvalue = ''; let count = 0; for (let i = integerPart.length - 1; i >= 0; i--) { count++; formattedvalue = integerPart[i] + formattedvalue; if (count % 3 === 0 && i !== 0) { formattedvalue = ',' + formattedvalue; } } // Combine the formatted integer part and decimal part (if present) if (decimalPart.length > 0) { formattedvalue += '.' + decimalPart; } return formattedvalue; }
Proper way to load a new Swiper with Elementor native SwiperJS
window.addEventListener('load',function(){ let swiperConfigObject = { } let checkElementorFrontendInterval = setInterval(function() { if (typeof elementorFrontend !== 'undefined') { clearInterval(checkElementorFrontendInterval); if ('undefined' === typeof Swiper) { const onDemandSwiper = elementorFrontend.utils.swiper; new onDemandSwiper(swiperContainerElem, swiperConfigObject).then((newSwiperInstance) => { swipere = newSwiperInstance; }); } else { swipere = new Swiper(swiperContainerElem, swiperConfigObject); } } }, 100); });
Proper way to load a new Swiper with Elementor native SwiperJS, more complete example
window.addEventListener('load', function () { if (document.querySelector('.elementor-editor-active')) { return; } let swiperConfigObject = { loop: true, slidesPerView: 1, spaceBetween: 0, } let checkElementorFrontendInterval = setInterval(function() { if (typeof elementorFrontend !== 'undefined') { clearInterval(checkElementorFrontendInterval); if ('undefined' === typeof Swiper) { const onDemandSwiper = elementorFrontend.utils.swiper; new onDemandSwiper(swiperContainerElem, swiperConfigObject).then((newSwiperInstance) => { postsSwiper = newSwiperInstance; }); } else { postsSwiper = new Swiper(swiperContainerElem, swiperConfigObject); } } }, 100); });
Add setting to remove loading="lazy" from Elementor images
add_action( 'elementor/element/image/section_image/before_section_end', function( $element, $args ) { $element->start_injection( [ 'at' => 'after', 'of' => 'link', ] ); $element->add_control( 'my_image_lazy_loading', [ 'label' => __( 'Lazy Loading','lazy-load-control-for-elementor' ), 'type' => \Elementor\Controls_Manager::SELECT, 'default' => 'lazy', 'options' => [ 'lazy' => __( 'Lazy load','lazy-load-control-for-elementor' ), 'no_lazy' => __( 'Do not lazy load','lazy-load-control-for-elementor' ), ], ] ); $element->end_injection(); }, 10, 2 ); add_action( 'elementor/widget/render_content', function( $content, $widget ){ if( $widget->get_name() === 'image' ){ $settings = $widget->get_settings(); if( isset( $settings['my_image_lazy_loading'] ) && 'no_lazy' === sanitize_text_field( $settings['my_image_lazy_loading'] ) ){ $content = str_replace( ' loading="lazy"','',$content ); } } return $content; }, 10, 2 );
Make entire AJAX loaded loop a link clickable to follow to post
<script> document.addEventListener('DOMContentLoaded', function() { document.addEventListener('click',function(event){ if (event.target.closest('.projectLoopWrapper')){ let url = event.target.closest('.projectLoopWrapper').querySelector('a[href]').href; location = url; } }); }); </script> <style> @media (min-width:1025px){ .projectLoopWrapper { cursor: pointer; transition:0.3s transform; transform: scale(0.96); } .projectLoopWrapper:hover { transform: scale(1); } } </style>
Sequential Animation on Listing Grids Items, should work for any custom loops
<script> (function () { if (document.querySelector('.elementor-editor-active')) { return; } let gridItems = document.querySelectorAll('.jet-listing-grid__item'); gridItems.forEach(manageGridItems) function manageGridItems(gridItem, i, arr) { let section = gridItem.querySelector('.elementor-section'); let settingsObject = JSON.parse(section.getAttribute('data-settings')); settingsObject.animation_delay = i * 200; section.setAttribute('data-settings', JSON.stringify(settingsObject)); } }()); </script>
From Josh W Comeau, debug "position:fixed" not working
// Replace “.the-fixed-child” for a CSS selector // that matches the fixed-position element: const selector = '.the-fixed-child'; function findCulprits(elem) { if (!elem) { throw new Error( 'Could not find element with that selector' ); } let parent = elem.parentElement; while (parent) { const { transform, willChange, filter, } = getComputedStyle(parent); if ( transform !== 'none' || willChange === 'transform' || filter !== 'none' ) { console.warn( '🚨 Found a culprit! 🚨\n', parent, { transform, willChange, filter } ); } parent = parent.parentElement; } } findCulprits(document.querySelector(selector));
minimal way to add a setting to an elementor element
add_action( 'elementor/element/gallery/overlay/after_section_end', function( $element, $args ) { $element->start_controls_section( 'grid_settings', [ 'label' => __( 'Grid Settings', 'text-domain' ), 'tab' => \Elementor\Controls_Manager::TAB_CONTENT, ] ); $element->add_control( 'grid_switch', [ 'label' => __( 'Grid Switch', 'text-domain' ), 'type' => \Elementor\Controls_Manager::SWITCHER, 'default' => 'yes', 'label_on' => __( 'On', 'text-domain' ), 'label_off' => __( 'Off', 'text-domain' ), ] ); $element->end_controls_section(); }, 10, 2 );
Reorder 4 containers that each have a category archive in them, based on how many posts are listed in each category.
<script> /* Will show categories with the most posts i them at the top. 4 post listings needed, each in their own container with class "eventCategory" . Parent wrapper needs class "eventCategoriesParent" container with no posts will be set to display none The other containers will be sorted in descending order */ document.addEventListener('DOMContentLoaded', function () { let categories = Array.from(document.querySelectorAll('.eventCategory')); let parentWrapper = document.querySelector('.eventCategoriesParent'); let listItemsQuantity = []; categories.forEach(category => { let listItems = category.querySelectorAll('.elementor-post'); if (listItems.length < 1) { category.style.display = 'none'; } listItemsQuantity.push(listItems.length); }); categories.sort((a, b) => listItemsQuantity.indexOf(categories.indexOf(a)) - listItemsQuantity.indexOf(categories.indexOf(b))); categories.forEach(category => parentWrapper.appendChild(category)); }); </script>
Horizontal Nav Elementor default Nav
#horizontalNav .elementor-nav-menu a, #horizontalNav .elementor-nav-menu li { position: static; } #horizontalNav .elementor-nav-menu { position: static; } #horizontalNav .sub-menu { display: flex!important; flex-direction: row; left: 0!important; margin-top: 0!important; margin-left: 0!important; right: 0; justify-content: start; pointer-events: none; opacity: 0; transition: opacity 0.3s; } #horizontalNav .has-submenu:hover + .sub-menu, #horizontalNav .sub-menu:hover, #horizontalNav .sub-menu:focus-within { opacity: 1; pointer-events: all; } #horizontalNav .elementor-nav-menu ul ul a, #horizontalNav .elementor-nav-menu ul ul a:active, #horizontalNav .elementor-nav-menu ul ul a:focus, #horizontalNav .elementor-nav-menu ul ul a:hover, #horizontalNav ul.elementor-nav-menu--dropdown a, #horizontalNav ul.elementor-nav-menu--dropdown a:focus, #horizontalNav ul.elementor-nav-menu--dropdown a:hover { border-left: none; } #horizontalNav .sub-menu .menu-item { /* width:100%; */ } #horizontalNav .sub-menu .menu-item:not(:last-child) { border-right: 1px solid #999999; } #horizontalNav .sub-menu .menu-item-has-children { position: relative; } #horizontalNav .sub-menu .sub-menu { justify-content: center; }
clamp
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
Show CTA content by default on mobile, without having to hover
@media (max-width:767px){ .elementor-cta .elementor-cta__content > *{ opacity:1; } .elementor-cta .elementor-cta__bg-overlay { background-color: #0F2133BF; } }
Horizontal Scroll Section (free tutorial) make it work with WooCommerce Product Grid and related products
.products.products.products.elementor-grid { display: grid; grid-auto-flow: column; } .elementor-products-grid .products.products.elementor-grid li.product { width: 300px; }
Add a progress bar to jet engine listing grid slick slider
<div class="slider-progress" role="progressbar" aria-valuemin="0" aria-valuemax="100"> <span class="slider__label sr-only"></span> </div> <style> .slider-progress { display: block; width: 100%; height: 1px; overflow: hidden; background-color: #999999; transform-origin: 0 50%; transform: scaleX(0); transition: transform .4s ease-in-out; } </style> <script> window.addEventListener('load', function () { waitFor(_ => document.querySelector('.jet-listing-grid__slider .slick-slider') && document.querySelector('.jet-listing-grid__slider .slick-slider').slick !== undefined) .then(_ => init()); function init(){ jQuery(function ($) { var $slider = $('.jet-listing-grid__slider .slick-slider'); var $progressBar = $('.slider-progress'); var $progressBarLabel = $('.slider__label'); $slider.on('beforeChange', function (event, slick, currentSlide, nextSlide) { var calc = ((nextSlide) / (slick.slideCount - 1)); $progressBar.css('transform', 'scaleX(' + calc + ')').attr('aria-valuenow', calc); $progressBarLabel.text(calc + '% completed'); }); }); } }); function waitFor(conditionFunction) { const poll = resolve => { if(conditionFunction()) resolve(); else setTimeout(_ => poll(resolve), 400); } return new Promise(poll); } </script>
Unselect radio input forms on click again vanilla javascript
(function(){ let radios = document.querySelectorAll('[type="radio"]'); let labelsOfRadio = document.querySelectorAll('[type="radio"] + label'); let arrOfState = []; labelsOfRadio.forEach((label, i) => { arrOfState[i] = false; label.addEventListener('click', function () { arrOfState[i] = !arrOfState[i]; if (!arrOfState[i]) { setTimeout(function () { radios[i].checked = false; }, 30); } arrOfState = arrOfState.map((e, index) => index !== i ? e = false : e = e); }); }); }());
Allow role=button to behave like a real <button> element by triggering the click on spacebar/enter keydown
/* Allow role=button to behave like a real <button> el. by triggering the click on spacebar/enter keydown */ document.querySelectorAll('div[role="button"]').forEach(el => { el.addEventListener('keydown', e => { const keyDown = e.key !== undefined ? e.key : e.keyCode; if ( (keyDown === 'Enter' || keyDown === 13) || (['Spacebar', ' '].indexOf(keyDown) >= 0 || keyDown === 32)) { /* prevent default so the page doesn't scroll when pressing space */ e.preventDefault(); el.click(); } }); });
Multi Row Accordion that works well on mobile also and is scrolling to the proper place
document.addEventListener('DOMContentLoaded', function () { jQuery(function ($) { function switcherGlideSmooth(openers, closers, content) { $(content).hide(); $(openers).css('cursor', 'pointer'); $(closers).css('cursor', 'pointer'); $(openers).attr('tabindex', 0); let contentWasOpened = false; $(openers).each(function (i) { $(this).on('click', function () { let currentOpener = $(this); $(openers).not(this).removeClass('switcher_active'); currentOpener.toggleClass('switcher_active'); let openedSection = $(content).eq(i); $(content).not(openedSection).slideUp(); openedSection.slideToggle(); scrollToProperPlace(currentOpener); currentOpener.hasClass('switcher_active') ? contentWasOpened = true : contentWasOpened = false; }); }); $(closers).each(function (j) { $(this).on('click', function () { $(openers).removeClass('switcher_active'); $(content).slideUp(); $('html, body').animate({ scrollTop: $(openers).eq(j).offset().top - 90, }, 'slow'); contentWasOpened = false; }); }); function scrollToProperPlace(currentOpener) { if (currentOpener.hasClass('switcher_active') && contentWasOpened) { setTimeout(() => { $('html, body').animate({ scrollTop: currentOpener.position().top + currentOpener.offset().top + currentOpener.outerHeight(true) - 110, }, 'slow'); }, 400); } else if (currentOpener.hasClass('switcher_active')) { $('html, body').animate({ scrollTop: currentOpener.position().top + currentOpener.offset().top + currentOpener.outerHeight(true) - 110, }, 'slow'); } else { $('html, body').animate({ scrollTop: currentOpener.offset().top - 90, }, 'slow'); } } } switcherGlideSmooth('.projectCard', '.closers', '.projectInfo'); }); });
Disable Elementor native smooth scroll
Elementor support provided the code below to disable smoothscroll script. Add to footer <script defer> jQuery( window ).on( 'elementor/frontend/init', function() { if ( typeof elementorFrontend === 'undefined' ) { return; } elementorFrontend.on( 'components:init', function() { elementorFrontend.utils.anchors.setSettings( 'selectors.targets', '.dummy-selector' ); } ); } ); </script>
slideDown and slideUp in vanilla JS
/* function in vanilla JS that replaces jQuery slideDown() */ function slideDown(el) { el.style.display = 'block'; let height = el.clientHeight; el.style.height = 0; el.style.overflow = 'hidden'; let y = 0; let step = function() { y += 10; if (y < height) { el.style.height = y + 'px'; requestAnimationFrame(step); } else { el.style.height = 'auto'; } }; step(); } /* function in vanilla JS that replaces jQuery slideUp() */ function slideUp(el) { el.style.height = el.clientHeight + 'px'; el.style.overflow = 'hidden'; let y = el.clientHeight; let step = function() { y -= 10; if (y > 0) { el.style.height = y + 'px'; requestAnimationFrame(step); } else { el.style.display = 'none'; } }; step(); }
Find furthest ancestor
function findFurthestAncestor(elem, className) { let parentElement = elem.parentElement; let furthestAncestor; while (parentElement != null) { if (parentElement.classList.contains(className)) { furthestAncestor = parentElement; } parentElement = parentElement.parentElement; } return furthestAncestor; }
start at the end swiper
<script> window.addEventListener('load', function () { setTimeout(() => { let swiperElems = document.querySelectorAll('.starting-at-end-slider .swiper-container'); swiperElems.forEach(swiperElem => { let swiper = swiperElem.swiper; swiper.slideTo(swiper.slides.length - 1); }) }, 2900); }); </script>
Very performant way to show something / add remove class name after scrolling Y pixel amount
<script> document.addEventListener('DOMContentLoaded', function () { let aboveThisPos = 300; /* Show up after scroling down 300px */ let targetElem = document.querySelector('.StickyNavIcons'); let observerOptions = { threshold: 0, rootMargin: () => { return `${aboveThisPos + 100}px 0px -${aboveThisPos + window.innerHeight}px 0px` }, } const inViewCallback = entries => { entries.forEach((entry) => { if (entry.isIntersecting) { targetElem.classList.add('isShown'); } else { targetElem.classList.remove('isShown'); } }); } let observer = new IntersectionObserver(inViewCallback, {threshold: observerOptions.threshold,rootMargin: observerOptions.rootMargin()}); observer.observe(document.body); let windowWidth = window.innerWidth; window.addEventListener('resize', () => { if (windowWidth === window.innerWidth) return; observer.unobserve(document.body); observer = new IntersectionObserver(inViewCallback, {threshold: observerOptions.threshold,rootMargin: observerOptions.rootMargin()}); observer.observe(document.body); }); }); </script>
Amazing Show HIde section with URL Anchors and auto open depending on anchor
/* Code from https://element.how/elementor-show-hide-section/ * Copyright 2023 https://element.how/ * No redistribution without permission * Use on your own and your client sites is allowed and welcomed */ document.addEventListener('DOMContentLoaded', function () { $ = jQuery; let toggles, contents, closers, closersAll, anchorToggles; function setupToggles() { toggles = document.querySelectorAll('.toggleElem'); contents = document.querySelectorAll('.toggleContent'); closers = document.querySelectorAll('.toggleClose'); closersAll = document.querySelectorAll('.toggleCloseAll'); anchorToggles = document.querySelectorAll('.toggleAnchor'); toggles.forEach((toggle, i) => { if (toggle.classList.contains('toggleIsSetup')) return; toggle.classList.add('toggleIsSetup'); toggle.addEventListener('click', function (e) { e.preventDefault(); toggle.classList.toggle('toggleIsActive'); jQuery(contents[i]).slideToggle(); if (toggle.classList.contains('toggleIsActive')) resizeEvent(); }); }); anchorToggles.forEach((anchorToggle, i) => { if (anchorToggle.classList.contains('toggleIsSetup')) return; anchorToggle.classList.add('toggleIsSetup'); anchorToggle.addEventListener('click', function (e) { e.preventDefault(); toggles[i].classList.add('toggleIsActive'); jQuery(contents[i]).slideDown(); if (toggles[i].classList.contains('toggleIsActive')) resizeEvent(); }); }); closers.forEach((closer, i) => { if (closer.classList.contains('toggleIsSetup')) return; closer.classList.add('toggleIsSetup'); closer.addEventListener('click', function (e) { e.preventDefault(); toggles[i].classList.remove('toggleIsActive'); jQuery(contents[i]).slideUp(); }); }); closersAll.forEach((closer, i) => { if (closer.classList.contains('toggleIsSetup')) return; closer.classList.add('toggleIsSetup'); closer.addEventListener('click', function (e) { e.preventDefault(); toggles.forEach((toggle, i) => { toggle.classList.remove('toggleIsActive'); }); contents.forEach((content, i) => { jQuery(content).slideUp(); }); }); }); contents.forEach(content => { if (content.classList.contains('toggleIsSetup')) return; content.classList.add('toggleIsSetup'); content.style.display = 'none'; }); } setupToggles(); /* compatibility for Elementor popups */ jQuery(document).on('elementor/popup/show', () => { setupToggles(); }); function resizeEvent() { setTimeout(function () { window.dispatchEvent(new Event('resize')); }, 400); } });
text path animation marquee svg
<svg class="svgwave" xmlns="http://www.w3.org/2000/svg" width="301" height="21" viewBox="0 0 301 21"> <path id="wavepath" d="M0,.5c30.0978,0,30.0978,20,60.1956,20S90.2948.5,120.394.5s30.1,20,60.2,20c30.1015,0,30.1015-20,60.203-20s30.1014,20,60.2029,20"></path> <text text-anchor="middle"> <textPath class="my-text" href="#wavepath" startOffset="50%"> <animate attributeName="startOffset" from="-114%" to="214%" begin="-15s" dur="30s" repeatCount="indefinite"></animate> habiter et se laisser transporter dès 2023    habiter et se laisser transporter dès 2023    habiter et se laisser transporter dès 2023     habiter et se laisser transporter dès 2023</textPath> </text> </svg> <svg class="svgwave svgwave2" xmlns="http://www.w3.org/2000/svg" width="301" height="21" viewBox="0 0 301 21"> <path id="wavepathe" d="M0,.5c30.0978,0,30.0978,20,60.1956,20S90.2948.5,120.394.5s30.1,20,60.2,20c30.1015,0,30.1015-20,60.203-20s30.1014,20,60.2029,20"></path> <text text-anchor="middle"> <textPath class="my-text" href="#wavepathe" startOffset="10%"> <animate attributeName="startOffset" from="-114%" to="214%" begin="0s" dur="30s" repeatCount="indefinite"></animate> habiter et se laisser transporter dès 2023    habiter et se laisser transporter dès 2023    habiter et se laisser transporter dès 2023     habiter et se laisser transporter dès 2023</textPath> </text> </svg> <style> svg.svgwave { width: 100%; height: auto; overflow: visible; } svg.svgwave path { fill: transparent; stroke: transparent; stroke-width: 1px; } svg.svgwave text { font-family: 'Gotham'; font-weight: 300!important; font-size: 6px!important; fill: #337179; } .svgwave2 { opacity: 0; animation: fadeeIn 0.8s forwards; left: 0; position: absolute; } @keyframes fadeeIn{ 0%{ opacity: 0; } 99%{ opacity: 0; } 100%{ opacity: 1; } } </style>
Fancy OffScreen Navigation with Content Of Page Scaling Down
function add_footer_scripts() { if (is_single(40961) || is_single(40963)) { // Render shortcode content of navigation container template $sibling_content = do_shortcode(''); ?> <div id="sibling-div" style="display:none;"> <?php echo $sibling_content; ?> </div> <script type="text/javascript"> (function () { // Fetch sibling div and remove it from the DOM temporarily let siblingDiv = document.getElementById('sibling-div'); siblingDiv.style.display = 'block'; document.body.removeChild(siblingDiv); let bodyChildren = Array.from(document.body.childNodes); let wrapper = document.createElement('div'); wrapper.id = 'core-wrapper'; let innerWrapper = document.createElement('div'); innerWrapper.id = 'inner-wrapper'; let pageContent = document.createElement('div'); pageContent.id = 'page-content-wrapper'; let selectors = ['.elementor-location-header', '#wpadminbar']; let elementsToMove = []; let nonMatchingElements = []; for (let i = 0; i < bodyChildren.length; i++) { let node = bodyChildren[i]; if (node.nodeType === 1 && node.tagName.toLowerCase() !== 'script' && node.tagName.toLowerCase() !== 'style') { let matchesSelector = selectors.some(selector => node.matches(selector)); if (matchesSelector) { elementsToMove.push(node); } else { nonMatchingElements.push(node); } } } nonMatchingElements.forEach(function (element) { pageContent.appendChild(element.cloneNode(true)); element.parentNode.removeChild(element); }); elementsToMove.forEach(function (element) { innerWrapper.appendChild(element.cloneNode(true)); element.parentNode.removeChild(element); }); innerWrapper.appendChild(pageContent); wrapper.appendChild(innerWrapper); let closeNav = document.createElement('button'); closeNav.id = 'closeNav'; document.body.insertBefore(closeNav, document.body.firstChild); document.body.insertBefore(wrapper, document.body.firstChild); document.body.insertBefore(siblingDiv, document.body.firstChild); })(); </script> <style> .elementor.elementor-location-header { position: fixed; top: 0; left: 0; width: 100%; z-index: 444; } .admin-bar .elementor-location-header { top: 32px; } .toggleElem { cursor: pointer; } :root { --default-pages-background-color: #ffffff; --navigation-opened-background-color: #777777; --close-icon-color: #333333; } html { overflow-x: hidden; overflow-y: scroll; /* to keep the width of the scroll bar on screen even while the nav is opened */ } body.fancyNav { background-color: var(--navigation-opened-background-color, #999999); } #sibling-div { position: fixed; right: 0; top: 0; z-index: 4; transition: transform var(--nav-timing), opacity var(--nav-timing); transform: translateX(30%); opacity: 0; } .fancyNavOpened #sibling-div { transform: translateX(0); opacity: 1; transition: transform var(--nav-timing) 0.08s, opacity var(--nav-timing) 0.08s; } #core-wrapper { z-index: 6; position: relative; background-color: var(--default-pages-background-color, #ffffff); transition: transform var(--nav-timing); } .fancyNavOpened #core-wrapper { transform: scale(0.78) translateX(-36vw); height: 90vh; } #closeNav { position: fixed; right: 38.9vw; top: 50%; width: 54px; height: 54px; z-index: 9; border-radius: 51%; border: 2px solid var(--close-icon-color); cursor: pointer; opacity: 0; pointer-events: none; transform: translate(50%, 0%); transition: transform 0.3s, opacity 0.14s; } .fancyNavOpened #closeNav { transform: translate(50%, -50%); opacity: 1; pointer-events: all; transition: transform 0.3s, opacity 0.3s 0.6s; } .fancyNavOpened #closeNav:hover { transform: translate(50%, -50%) scale(1.2); } #closeNav:before, #closeNav:after { position: absolute; content: ' '; width: 30px; height: 2px; background-color: var(--close-icon-color); } #closeNav:before { transform: translate(-50%, -50%) rotate(45deg); } #closeNav:after { transform: translate(-50%, -50%) rotate(-45deg); } #navContainer { height: 100vh; height: 100dvh; overflow-y: auto; width: 30vw; padding: 60px 3vw; background-color: var(--navigation-opened-background-color, #999999); } @media (max-width:1023px) { #navContainer { width: 50vw; padding: 60px 3vw; } .fancyNavOpened #core-wrapper { transform: scale(0.78) translateX(-64vw); } #closeNav { right: 60.8vw; width: 44px; height: 44px; } #closeNav:before, #closeNav:after { width: 23px; height: 2px; } } @media (max-width:767px) { #navContainer { width: 78vw; padding: 60px 3vw; } .fancyNavOpened #core-wrapper { transform: scale(0.78) translateX(-90vw); } #closeNav { right: 81.4vw; width: 34px; height: 34px; } #closeNav:before, #closeNav:after { width: 18px; height: 2px; } } </style> <script> document.addEventListener('DOMContentLoaded', function () { let toggleElem = document.querySelector('.toggleElem'); let nav = document.querySelector('#sibling-div'); let coreWrapper = document.querySelector('#core-wrapper'); let pageContentWrapper = document.querySelector('#page-content-wrapper'); let navIsOpened = false; let scrollY = window.scrollY; let closeNavEvent; let closeNavButton = document.getElementById('closeNav'); document.body.classList.add('fancyNav'); let navTiming = 600; /* add custom property --nav-timing to <html> element */ document.documentElement.style.setProperty('--nav-timing', navTiming + 'ms'); // Toggle is-active class on click toggleElem.addEventListener('click', function () { navIsOpened ? closeNav() : openNav(); }); closeNavButton.addEventListener('click', function () { closeNav(); }); function openNav() { scrollY = isAdminBarVisible()? window.scrollY - 32 : window.scrollY; document.body.classList.add('fancyNavOpened'); pageContentWrapper.style.position = 'relative'; pageContentWrapper.style.top = scrollY * -1 + 'px'; coreWrapper.style.overflow = 'hidden'; coreWrapper.style.height = '100vh'; navIsOpened = true; if (typeof ScrollTrigger === 'function') ScrollTrigger.disable(false); setTimeout(function () { coreWrapper.addEventListener('click', closeNavEvent = function () { toggleElem.click(); }); }, navTiming); } function closeNav() { document.body.classList.remove('fancyNavOpened'); coreWrapper.removeEventListener('click', closeNavEvent); navIsOpened = false; setTimeout(function () { pageContentWrapper.style.removeProperty('position'); pageContentWrapper.style.removeProperty('top'); coreWrapper.style.removeProperty('overflow'); coreWrapper.style.removeProperty('height'); window.scrollTo(0, scrollY); if (typeof ScrollTrigger === 'function') ScrollTrigger.enable(false); }, navTiming); } function isAdminBarVisible() { return document.getElementById('wpadminbar') ? true : false; } // Remove is-active class on pressing Esc document.addEventListener('keydown', function (event) { if (event.key === 'Escape') { closeNav(); } }); }); </script> <?php } } add_action('wp_footer', 'add_footer_scripts');
Autoscroll list of items that are in a horizontally scrollable div to center of clicked item
document.addEventListener('DOMContentLoaded',function(){ let scrollableNavigations = document.querySelectorAll('.jet-radio-list-wrapper'); scrollableNavigations.forEach((scrollableNavigation, index) => { let scrollingContainer = scrollableNavigation.parentElement; let navigationItems = scrollableNavigation.querySelectorAll('.jet-radio-list__row.jet-filter-row'); scrollingContainer.style.scrollBehavior = "smooth"; navigationItems.forEach((navigationItem,i) => { let scrollToPosition = (navigationItem.getBoundingClientRect().left + navigationItem.offsetWidth/2) - (window.innerWidth/2); navigationItem.addEventListener('click',function(event){ scrollingContainer.scrollTo(scrollToPosition, 0); }); }); }); });
Jet Smart filters jetsmartfilters set Saturday as first day of week for date picker
/* $0 is the input elem, same idea shouold work for any air datepicker */ jQuery($0).data().datepicker.opts.firstDay = 6; jQuery($0).data().datepicker.update();
JetSmartFilters filter event
window.addEventListener('load',function(){ window.JetSmartFilters.events.subscribe('ajaxFilters/end-loading', ()=>{ updateProductsSalesAmount(); }); });
Stacked card slider out of Slick slider, has to have the "center" option enabled
selector .jet-listing-grid__item { padding: 20px; -webkit-box-sizing: border-box; box-sizing: border-box; } selector .jet-listing .slick-slider .slick-list { padding: 0 80px; margin-left: -40px; margin-right: -40px; } selector .slick-slide { transform: translateX(90%) scale(0.9); transition: 0.6s transform; } selector .slick-slide.slick-current.slick-active ~ .slick-slide { transform: translateX(-90%) scale(0.9); z-index: -3; position: relative; } selector .slick-slide.slick-current.slick-active { transform: translateX(0) scale(1); z-index: 3; } selector .slick-slide.slick-slide.slick-slide.slick-cloned.slick-center { transform: translateX(0px); position: relative; z-index: 3; } @media (max-width: 767px) { selector .jet-listing .slick-slider .slick-list { padding: 0 80px; margin-left: -50px; margin-right: -50px; } } /* .jet-listing-grid__item { position:relative; } .jet-listing-grid__item::after { content:''; position:absolute; width:100%; height:100%; left:0; top:0; z-index:-3; background: #FFFFFF44; box-shadow: 0px 0px 18px #00000038; border-radius: 9px; opacity: 0.9; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); } */
On close popup remove class of hamburger icon
window.addEventListener('load',function(){ jQuery( document ).on( 'elementor/popup/hide', ( event, id, instance ) => { if ( id === 123 ) { $('.hamburger').removeClass('.is-active'); } }); });
Limit carousel lightboxes to opening only the images within the carousel or galleries (instead of all the carousels on the page)
/* * Copyright 2022 Maxime Desrosiers */ function wp_fix_lightbox_for_dynamic_carousels_and_galleries() { ?> <script> window.addEventListener('load', function () { //Limit carousel lightboxes to opening only the images within the carousel or galleries (instead of all the carousels on the page) let carouselsAndGalleries = document.querySelectorAll('.elementor-image-gallery, .elementor-image-carousel-wrapper'); /* excludes gallery pro for now, add when needed */ carouselsAndGalleries.forEach((e, i) => { e.querySelectorAll('a').forEach(anchor => anchor.setAttribute('data-elementor-lightbox-slideshow', '34532' + i)); }); }); </script> <?php } add_action( 'wp_footer', 'wp_fix_lightbox_for_dynamic_carousels_and_galleries');
Single click to go forward a slide in a Swiper slider
window.addEventListener('load', function () { if (window.innerWidth < 1024) return; /* disable on mobile */ setTimeout(() => { let swiperElems = document.querySelectorAll('.click-to-navigate .swiper-container'); swiperElems.forEach(swiperElem => { let swiper = swiperElem.swiper; swiperElem.addEventListener('click', function () { swiper.slideNext(); }) }) }, 2900); });
WordPress Image size check on upload
/* Marc Dingena Utilities * Test image resolution before image crunch */ add_filter('wp_handle_upload_prefilter','mdu_validate_image_size'); function mdu_validate_image_size( $file ) { $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'] ); //if it's not an image, don't run the rest of the function if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) { return $file; } $image = getimagesize($file['tmp_name']); $minimum = array( 'width' => '400', 'height' => '400' ); $maximum = array( 'width' => '2000', 'height' => '2000' ); $image_width = $image[0]; $image_height = $image[1]; $too_small = "Image dimensions are too small. Minimum size is {$minimum['width']} by {$minimum['height']} pixels. Uploaded image is $image_width by $image_height pixels."; $too_large = "Image dimensions are too large. Maximum size is {$maximum['width']} by {$maximum['height']} pixels. Uploaded image is $image_width by $image_height pixels."; if ( $image_width < $minimum['width'] || $image_height < $minimum['height'] ) { // add in the field 'error' of the $file array the message $file['error'] = $too_small; return $file; } elseif ( $image_width > $maximum['width'] || $image_height > $maximum['height'] ) { //add in the field 'error' of the $file array the message $file['error'] = $too_large; return $file; } else return $file; }
Swiper flickering / flash issue fix
/* Fix flickering issue on ipads when changing slide, more info : https://github.com/nolimits4web/swiper/issues/3527 */ @media (max-width:1370px) and (-webkit-min-device-pixel-ratio: 2) { .elementor-lightbox .swiper-container img{ backdrop-filter: blur(3.5px); -webkit-backdrop-filter: blur(3.5px); -moz-backdrop-filter: blur(3.5px); } }
GSAP horizontal galleries with captions, auto height images, works well everywhere
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/gsap.min.js" integrity="sha512-VEBjfxWUOyzl0bAwh4gdLEaQyDYPvLrZql3pw1ifgb6fhEvZl9iDDehwHZ+dsMzA0Jfww8Xt7COSZuJ/slxc4Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/ScrollTrigger.min.js" integrity="sha512-v8B8T8l8JiiJRGomPd2k+bPS98RWBLGChFMJbK1hmHiDHYq0EjdQl20LyWeIs+MGRLTWBycJGEGAjKkEtd7w5Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script> window.addEventListener('load', function () { gsap.registerPlugin(ScrollTrigger); ScrollTrigger.defaults({ scroller: document.documentElement }); ScrollTrigger.config({ /* autoRefreshEvents: "visibilitychange,load", */ /* Remove the comments here to narrow down even more the auto refresh events, although that will likely cause problems on desktop when resizing the screen */ limitCallbacks: true, ignoreMobileResize: true }); /* THE FOLLOWING CODE is NOT WORKING FULLY - CAUSING some random scroll to actions when changing orientation */ /* on orientation change, refresh all scrolltrigger instances window.addEventListener('orientationchange', function () { setTimeout(function () { ScrollTrigger.refresh(); }, 1340); }); */ /* Show overlay on swipe left and right */ let shownAlready = false; let infoOverlay = document.querySelector('.infoOverlay'); function showOverlay() { if (infoOverlay && !shownAlready) { shownAlready = true; infoOverlay.style.opacity = 0.81; /* opacity of the overlay*/ setTimeout(() => { infoOverlay.style.opacity = 0; }, 3400); /* this sets how long the overlay is displayed */ } } /* End of Show overlay on swipe left and right, although some code below is also needed */ let parentsOfStickySection = document.querySelectorAll('.horizontalScroll_parent'); let clickFromGalleryItem = false; let stickyParent; /* loop through every horizontal scroll section on the page, initiate the horizontal scroll for each one of them */ parentsOfStickySection.forEach(parentOfStickySection => { /* Show overlay on swipe left and right */ ScrollTrigger.observe({ target: parentOfStickySection, type: "touch", tolerance: 200, onRight: showOverlay, onLeft: showOverlay, }); /* End of Show overlay on swipe left and right, although some code above is also needed */ /* Horizontal Scroll Section */ let translateXSection = parentOfStickySection.querySelector('.horizontalScroll_translateContainer'); let horizontalHeader = parentOfStickySection.querySelector('.horizontalScroll_flexHeaderWrapper'); /* GSAP animation settings */ let options = { x: () => -(translateXSection.scrollWidth - window.innerWidth), ease: "none", scrollTrigger: { trigger: parentOfStickySection, pin: true, scrub: 1, /* "smooth scrolling" amount, can try 2 or 3 */ invalidateOnRefresh: true, /* recalculate everything on refresh, needed to work well with window resizes */ end: () => "+=" + (translateXSection.scrollWidth - window.innerWidth), } }; /* To show and hide the .horizontalScroll_flexHeaderWrapper horizontal headers inner sections after scrolling 600px horizontally */ function checkIfFarEnough() { if (horizontalHeader) { horizontalHeader.classList.add('horizontalScroll_flexHeaderWrapper--hasScrolled') let amountOfPxs = +window.getComputedStyle(translateXSection).transform.split(',')[4]; if (amountOfPxs < -600) { horizontalHeader.classList.add('horizontalScroll_flexHeaderWrapper--0opacity'); } else { horizontalHeader.classList.remove('horizontalScroll_flexHeaderWrapper--0opacity'); } } } /* reliable way to show or hide the horizontalHeader, works even if scrolling quickly back up */ setInterval(() => { checkIfFarEnough(); }, 300); /* End of To show and hide the .horizontalScroll_flexHeaderWrapper horizontal headers */ /* initiate the animation */ let anim = gsap.to(translateXSection, options); /* THE FOLLOWING CODE is NOT WORKING FULLY - CAUSING THE NORMAL SCROLL TO STOP WHEN GSAP IS LOADED */ /* refresh the GSAP scrolltrigger timeline with the IntersectionObserver API, so that it is initiated only when in view - In an attempt to properly calculate the height and positioning of the horizontal scroll parts let animInit; const observerOptions = { threshold: 0, rootMargin: '0px 0px -500px 0px', } const inViewCallback = entries => { entries.forEach((entry, i, arr) => { if (entry.isIntersecting && !animInit) { anim.scrollTrigger.refresh(); animInit = true; } }); } let observer = new IntersectionObserver(inViewCallback, observerOptions); observer.observe(parentOfStickySection); End of IntersectionObserver GSAP refresh */ /* optional: before opening lightbox, scroll to center of clicked image - this can likely be turned off and site/functions will still look good */ let imageElems = parentOfStickySection.querySelectorAll('.gallery-item'); imageElems.forEach((imageElem, i) => { imageElem.addEventListener('click', function () { scrollToViewedImage(parentOfStickySection, i); clickFromGalleryItem = true; }); }); }); /* End of scroll to center of clicked image before opening lightbox */ /* function used to scroll o the viewewd image, used by the "scroll to center of clicked image" above, and by the code below to scroll the the image viewed in the lightbox */ function scrollToViewedImage(parentOfStickySection, i) { let imageElem = parentOfStickySection.querySelectorAll('.elementor-image-gallery .gallery-item')[i]; let scrollThere = parentOfStickySection.parentElement.getBoundingClientRect().top + window.scrollY; scrollThere += imageElem.offsetLeft + (imageElem.offsetWidth / 2) - (window.innerWidth / 2); window.scrollTo(0, scrollThere); } /* Scroll in the page to the image that is currently viewed in the lightbox */ document.addEventListener('click', function (event) { if (event.target.closest('.horizontalScroll_parent .elementor-image-gallery')) { clickFromGalleryItem = true; stickyParent = event.target.closest('.horizontalScroll_parent'); } else if (!event.target.closest('.elementor-lightbox')) { clickFromGalleryItem = false; eventListenerAdded = false; } setTimeout(function () { if (clickFromGalleryItem && document.querySelector('.elementor-lightbox .swiper-slide-active')) { let swiper = document.querySelector('.elementor-lightbox .swiper-container').swiper; swiper.on('realIndexChange', function () { setTimeout(function () { let i = document.querySelector('.elementor-lightbox .swiper-slide-active').getAttribute('data-swiper-slide-index'); scrollToViewedImage(stickyParent, i); }, 110); }); } }, 940); }); /* End of scroll to the image that is currently viewed in the lightbox */ }); /* On Page load, preloader opacity transition on horizontalScroll_flexHeaderWrapper and horizontalScroll_innerWrapper */ document.addEventListener('DOMContentLoaded', function () { setTimeout(function () { let flexHeaderWrappers = document.querySelectorAll('.horizontalScroll_flexHeaderWrapper'); let innerWrappers = document.querySelectorAll('.horizontalScroll_innerWrapper'); flexHeaderWrappers.forEach(e => e.classList.add('page_loaded')); innerWrappers.forEach(e => e.classList.add('page_loaded')); }, 610); }); /* End of preloader opacity transition on horizontalScroll_flexHeaderWrapper and horizontalScroll_innerWrapper */ /* Make long captions be 67% of image width */ document.addEventListener('DOMContentLoaded', function () { sizeCaptions(); window.addEventListener('resize', sizeCaptions); function sizeCaptions() { let horizontalImages = document.querySelectorAll('.horizontalScroll_translateContainer .gallery-item img'); horizontalImages.forEach(horizontalImage => { let imageWidth = horizontalImage.offsetWidth; let caption = horizontalImage.closest('.gallery-item').querySelector('.gallery-caption'); if (caption) { caption.style.width = `${imageWidth * .67}px`; /* Change the .67 here to .70, for exmaple, to make it 70% of image width */ } }); } }); /* End of make captions be 67% of image width */ /* Calculate height of images automatically, the gallery basic element needs to have the class name variableHeightImages */ document.addEventListener('DOMContentLoaded', function () { const clamp = (num, min, max) => Math.min(Math.max(num, min), max); let images = document.querySelectorAll('.variableHeightImages img'); let vhInPX = window.innerHeight; let isMobile = (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) || (/Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.platform)) || (navigator.maxTouchPoints && navigator.maxTouchPoints > 2 && /MacIntel/.test(navigator.platform))); images.forEach(image => { let aspectRatio = image.height / image.width; let imageSize = 17 * aspectRatio + 41; let withinBoundsImageSize = clamp(imageSize, 40, 75); /* stay within 40vh and 75vh, no matter the aspect ratio */ if (isMobile) { image.style.height = (withinBoundsImageSize / 100) * vhInPX + 'px'; /* on mobile, set height of images in PX. This was an attempt to prevent the bug on certain browsers where URL bar showing up or hiding was messing with the height of the GSAP containers */ } else { image.style.height = withinBoundsImageSize + 'vh'; } }); }); /* End of calculate height of images automatically */ </script> <style> /* Style for horizontal scroll sections */ html, body { overflow-x: hidden; } body:not(.elementor-editor-active) .horizontalScroll_innerWrapper { top: 0; display: flex; align-items: center; } body:not(.elementor-editor-active) .horizontalScroll_innerWrapper>div>div>div.elementor-widget-wrap.elementor-element-populated { flex-direction: row; flex-wrap: nowrap; } .horizontalScroll_translateContainer.horizontalScroll_translateContainer.horizontalScroll_translateContainer, .horizontalScroll_translateContainer.horizontalScroll_translateContainer>.elementor-widget-wrap { width: max-content; } /* uniform padding for horizontal scroll sections - added by sven */ .horizontalScroll_flexHeaderWrapper { padding: 5% 5% 2.5% 5%; } @media (max-width: 767px) { .horizontalScroll_flexHeaderWrapper { padding-bottom:5%; }} .horizontalScroll_innerWrapper { padding: 0 5% 5% 5%; } .horizIntrotext_wrapper > div { margin:0!important; padding:0!important; } .horiz-introtext-header{ margin-top:-6px; /* to make it start on same visual height as small intro text next to it */ margin-left:-3px; /* to make it start visually at border of first image */ } @media (max-width: 767px) { .horiz-introtext-header{ margin-top:-6px; /* to make it start on same visual height as small intro text next to it */ margin-left:-2px; /* to make it start visually at border of first image */ }} .horiz-introtext{/* adjust global small intro text settings here*/ } .horiz-introtext p:last-child {/* Make sure the last p doesnt add space to next section */ margin: 0!important; } /* preloader opacity transition on horizontalScroll_flexHeaderWrapper and horizontalScroll_innerWrapper */ body:not(.elementor-editor-active) .horizontalScroll_flexHeaderWrapper{ transition: 1.25s opacity .4s; opacity: 0; } body:not(.elementor-editor-active) .horizontalScroll_innerWrapper { transition: 1.25s opacity .4s; opacity: 0; } body:not(.elementor-editor-active) .horizontalScroll_flexHeaderWrapper.page_loaded, body:not(.elementor-editor-active) .horizontalScroll_innerWrapper.page_loaded { opacity: 1; } /* Fade in/out the header of the horizontal scroll section */ .horizontalScroll_flexHeaderWrapper.horizontalScroll_flexHeaderWrapper.horizontalScroll_flexHeaderWrapper.horizontalScroll_flexHeaderWrapper--hasScrolled{ transition: 1.6s opacity; } .horizontalScroll_flexHeaderWrapper.horizontalScroll_flexHeaderWrapper.horizontalScroll_flexHeaderWrapper.horizontalScroll_flexHeaderWrapper--0opacity { pointer-events: none; opacity: 0; } /* Info Overlay on swipe left and right instead of scrolling up down " */ .infoOverlay { opacity:0; pointer-events:none; transition:opacity 0.8s; display:flex; position:fixed; height:100%; width:100%; top:0; left:0; align-items:center; justify-content:center; padding: 20px 20px 20px 90px; font-size: 17px; font-weight: 600; line-height: 1.25; background-color:#FFFFFF; color:#a6b04a; z-index:9999; text-align: left; /* was center */ } .infoOverlay::before { display: block; position: absolute; transform: translateY(-50%); top: 50%; left: calc(50% - 100px); width: 44px; height: 70px; border: 3px solid #a6b04a; border-radius: 22px; content: ""; } .infoOverlay::after { display: block; position: absolute; width: 3px; height: 3px; background: #a6b04a; border-radius: 1px; content: ""; animation-name: scrolle; animation-duration: 2s; animation-iteration-count: infinite; animation-delay: 2s; transform-origin: 0 0; top: calc(50% - 17px); left: calc(50% - 79px); } @keyframes scrolle { 0% { transform: scaleY(1); } 30% { transform: scaleY(6); } 40% { transform: translateY(19px); opacity: 1; } 41% { opacity: 0; } 81% { opacity: 0; } 100% { transform: translateY(0) scaleY(1); } } </style> <div class="infoOverlay"> Please scroll<br>up or down </div>
Woocommerce prevent duplicate Cart and Total after AJAX cart update, bug from Elementor pro
<script> /* prevent duplicate Cart and Total after AJAX cart update */ document.addEventListener('DOMContentLoaded', function() { jQuery(function($){ $( document.body ).on( 'updated_cart_totals', function(){ $('.woocommerce-cart-form:not(:first)').remove(); $('.cart_totals:not(:first)').remove(); }); }); }); </script>
Dynamic popup based on data attributes on the buttons that clicked to open them
/* elementor-popup-modal-24692 data-popup-title|Hey there data-popup-text|get this product, it's wonderful data-popup-button-text|Buy now data-popup-button-url|#test popupTitle popupText popupButton */ document.addEventListener('DOMContentLoaded', function () { let popupTriggers = document.querySelectorAll('.popupTriggers'); let newTitle, newText, newButtonText, newButtonURL; popupTriggers.forEach(popupTrigger => { popupTrigger.addEventListener('click', function () { newTitle = popupTrigger.getAttribute('data-popup-title'); newText = popupTrigger.getAttribute('data-popup-text'); newButtonText = popupTrigger.getAttribute('data-popup-button-text'); newButtonURL = popupTrigger.getAttribute('data-popup-button-url'); }); }); jQuery(document).on('elementor/popup/show', (event, id, instance) => { if (id === 24692) { let title = document.querySelector('.popupTitle h2'); let text = document.querySelector('.popupText p'); let button = document.querySelector('.popupButton a'); title.innerHTML = newTitle; text.innerHTML = newText; button.innerHTML = newButtonText; button.href = newButtonURL; } }); });
Elementor sticky effects fallback code if after optimization it's not reliable
<script> document.addEventListener('DOMContentLoaded', function () { let aboveThisPos = 300; /* Show up after scroling down 300px */ let targetElem = document.querySelector('#target-header'); let observerOptions = { threshold: 0, rootMargin: () => { return `${aboveThisPos + 100}px 0px -${aboveThisPos + window.innerHeight}px 0px` }, } const inViewCallback = entries => { entries.forEach((entry) => { if (entry.isIntersecting) { targetElem.classList.add('elementor-sticky--effects'); } else { targetElem.classList.remove('elementor-sticky--effects'); } }); } let observer = new IntersectionObserver(inViewCallback, {threshold: observerOptions.threshold,rootMargin: observerOptions.rootMargin()}); observer.observe(document.body); let windowWidth = window.innerWidth; window.addEventListener('resize', () => { if (windowWidth === window.innerWidth) return; observer.unobserve(document.body); observer = new IntersectionObserver(inViewCallback, {threshold: observerOptions.threshold,rootMargin: observerOptions.rootMargin()}); observer.observe(document.body); }); }); </script>
Make the navigation with sub items behave like an accordion on mobile, for the default Nav Menu element from Elementor
<script> /* Make mobile nav items behave like accordion, with only one opened at a time */ window.addEventListener('load', accordionBehaviourNavigationOnMobile); function accordionBehaviourNavigationOnMobile() { setTimeout(function () { let firstLevelNavItemsWithSub = Array.from(document.querySelectorAll('nav > ul > li .has-submenu')); let hasRun = false; firstLevelNavItemsWithSub.forEach(navitem => { navitem.addEventListener('click', function (event) { event.preventDefault(); if (!hasRun) { hasRun = true; let allButCurrent = firstLevelNavItemsWithSub.filter(e => e !== navitem && e.getAttribute('aria-expanded') == 'true'); allButCurrent.forEach(item => item.click()); } setTimeout(function () { hasRun = false }, 300); }); }); }, 1700); } </script>
Elementor Images Preloader
function wp_elementor_images_preloader() { ?> <style> .imagePreloader { position: absolute; width: 100%; height: 100%; left: 0; top: 0; transition: opacity .1s ease-out, display 0s .1s; transform: translateZ(0); } /* this pseudo element useless for now, might be useful if a specific color is wanted for the overlay instead of lowering opacity */ .imagePreloader::after { content:''; /* background: #999999; */ position: absolute; width: 100%; height: 100%; left: 0; top: 0; opacity: 0.1; } .spinner { width: 40px; height: 40px; position: absolute; left: 50%; top: 50%; margin-top: -20px; margin-left: -20px; } .spinner__side { position: absolute; width: 20px; height: 40px; top: 0; left: 0; overflow: hidden; } .side--right { left: 20px; } .spinner__fill { position: absolute; top: 0; left: 0; width: 40px; height: 40px; opacity: .6; border-radius: 999px; } .side--left .spinner__fill { border-left: 20px solid rgba(0, 0, 0, 0.15); -webkit-animation: cssSpinnerLeft 1.8s infinite linear; animation: cssSpinnerLeft 1.8s infinite linear; } .side--right .spinner__fill { border-right: 20px solid rgba(0, 0, 0, 0.15); -webkit-animation: cssSpinnerRight 1.8s infinite linear; animation: cssSpinnerRight 1.8s infinite linear; left: -20px; } .elementor-element img.loaded+.imagePreloader { opacity: 0; display: none; } .elementor-element img { opacity:0.7; } .elementor-element img.loaded { opacity:1; } /*! CSS Used keyframes */ @keyframes cssSpinnerLeft{0%{transform:rotate(0deg);}25%{transform:rotate(0deg);}35%{transform:rotate(90deg);}50%{transform:rotate(180deg);}75%{transform:rotate(180deg);}80%{transform:rotate(270deg);}100%{transform:rotate(360deg);}} @keyframes cssSpinnerRight{0%{transform:rotate(0deg);}20%{transform:rotate(90deg);}25%{transform:rotate(180deg);}50%{transform:rotate(180deg);}70%{transform:rotate(270deg);}75%{transform:rotate(360deg);}100%{transform:rotate(360deg);}} </style> <script> (function(){ if (document.querySelector('.elementor-editor-active')){ return; } let images = document.querySelectorAll('.elementor-element img'); images.forEach(image => { image.insertAdjacentHTML('afterEnd',`<div class="imagePreloader"><div class="spinner"> <span class="spinner__side side--left"><span class="spinner__fill"></span></span> <span class="spinner__side side--right"><span class="spinner__fill"></span></span> </div> </div>`); image.parentElement.parentElement.style.position = 'relative'; image.addEventListener('load',function(){ image.classList.add('loaded'); }) }) window.addEventListener('load',function(){ images.forEach(image => image.classList.add('loaded') ); }); }()); </script> <?php } add_action( 'wp_footer', 'wp_elementor_images_preloader');
Autoplaying accordion works for tabs and toggle also with small class name adjustements
<script> window.addEventListener('load', function() { let duration = 10000; let accordionElems = document.querySelectorAll('.autoplaying-accordion'); let isMobile = window.outerWidth < 768; let mobileClick = false; accordionElems.forEach(accordionElem => { let accordionTitles = accordionElem.querySelectorAll('.elementor-tab-title'); let currentIndex = 0; let titlesLength = accordionTitles.length; let switchActiveInt = setInterval(switchActiveAccordion, duration); /* start autoplay on page load */ setTimeout(function() { accordionTitles[0].classList.add('autoplaying') }, 530); accordionTitles.forEach((title, i, arr) => { title.addEventListener("click", function() { currentIndex = i; arr.forEach(e => e.classList.remove('autoplaying')); if (!mobileClick){ setTimeout(function() { title.classList.add('autoplaying'); }, 30); } }); }); /* stop autoplay on accordion hover */ accordionElem.addEventListener("mouseenter", function() { clearInterval(switchActiveInt); accordionTitles.forEach(e => e.classList.remove('autoplaying')); if (isMobile){ mobileClick = true; accordionTitles.forEach(e => e.classList.add('no-autoplay')); } }); /* resume autoplay on accordion mouse leave */ accordionElem.addEventListener("mouseleave", function() { if (!mobileClick){ switchActiveInt = setInterval(switchActiveAccordion, duration); accordionElem.querySelector('.elementor-active').classList.add('autoplaying'); } }); function switchActiveAccordion() { currentIndex + 1 < titlesLength ? currentIndex++ : currentIndex = 0; accordionTitles[currentIndex].click(); } }); }); </script> <style> .autoplaying-accordion .elementor-accordion-item .elementor-tab-title{ position: relative; } .autoplaying-accordion .elementor-accordion-item .elementor-tab-title.elementor-active::before { content: ''; position: absolute; width: 100%; left: 0; top: 0; background: #999; height: 3px; } .autoplaying-accordion .elementor-accordion-item .elementor-tab-title.elementor-active::after { content: ''; position: absolute; width: 100%; left: 0; top: 0; background: #FFF; height: 3px; transition: transform 0s; transform:scaleX(0); transform-origin:left; } .autoplaying-accordion .elementor-accordion-item .elementor-tab-title.elementor-active.autoplaying::after { transition: transform 8s linear; transform:scaleX(1); } /* To remove progress bar on mobile after click */ .autoplaying-accordion .elementor-accordion-item .elementor-tab-title.no-autoplay.elementor-active::before{ content:none; } </style>
scrol back up the page button with nice CSS animation
<a href="#top" class="backUpButton"></a> <style> html { scroll-behavior: smooth; } .backUpButton { width: 60px; height: 60px; position: fixed; right: 14px; bottom: 14px; background-color: #000000; border: 2px #ffffff solid; border-radius: 50%; z-index: 99; opacity: 0; pointer-events: none; transition: opacity 0.4s; cursor: pointer; } .backUpButton.isShown { opacity: 1; pointer-events: all; } .backUpButton::before { content: ''; position: absolute; width: 21px; height: 21px; border-top: 2px #ffffff solid; border-left: 2px #ffffff solid; left: 50%; top: 50%; transform: translate(-50%, -25%) rotate(45deg); transition: transform 0.6s ease; } .backUpButton:hover::before { transform: translate(-50%, -50%) rotate(45deg); } .backUpButton::after { content: ''; position: absolute; width: 2px; height: 23px; background-color: #ffffff; left: 50%; top: 50%; transform: translate(-50%, -25%) scaleY(0); transition: transform 0.6s ease; transform-origin: 50% 0; } .backUpButton:hover::after { transform: translate(-50%, -50%) scaleY(1); } </style> <script> document.addEventListener('DOMContentLoaded', function () { let scrollYPos = window.scrollY; let aboveThisPos = window.innerHeight * 2; /* Show up after scroling down ~200vh */ let targetElem = document.querySelector('.backUpButton'); let scrolling = false; /* For throlling scroll event */ window.addEventListener('scroll', function () { scrolling = true; }); setInterval(() => { if (scrolling || window.scrollY === 0) { scrolling = false; if (scrollYPos > aboveThisPos) { targetElem.classList.add('isShown'); } else { targetElem.classList.remove('isShown'); } scrollYPos = window.scrollY; } }, 600); }); </script>
Remove the scrollbar CSS
/* Remove scrollbar for Chrome, Safari and Opera */ .container::-webkit-scrollbar { display: none; } /* Remove scrollbar for IE, Edge and Firefox */ .container { -ms-overflow-style: none; /* IE and Edge */ scrollbar-width: none; /* Firefox */ }
Change video on hover of elements
window.addEventListener('load', function () { function initiateHoverDesign() { let crossFadeContainers = document.querySelectorAll('.cross_fade_videos_container'); crossFadeContainers.forEach(container => { let videosChangers = container.querySelectorAll('.videos_changers'); let videoElem = container.querySelector('.elementor-background-video-hosted'); let originalSrc = videoElem.src; console.log('original source = ' + originalSrc); let videoList = document.querySelector('.video_list'); let hoverVideos = []; videosChangers.forEach(changer => { if (changer.hasAttribute('data-hover-video')) { hoverVideos.push(changer.getAttribute('data-hover-video')); } else { throw "data-hover-video attribute is required for all elements with the class name 'videos_changers' " } }); let timeoutTracker; videosChangers.forEach((videosChanger, i) => { videosChanger.addEventListener('mouseenter', function () { videoElem.classList.add('video_transitioning'); clearTimeout(timeoutTracker); timeoutTracker = setTimeout(function () { videoElem.classList.remove('video_transitioning'); videoElem.src = hoverVideos[i]; videoElem.play(); }, 200); }); }); videoList.addEventListener('mouseleave', function () { videoElem.classList.add('video_transitioning'); clearTimeout(timeoutTracker); timeoutTracker = setTimeout(function () { videoElem.classList.remove('video_transitioning'); videoElem.src = originalSrc; videoElem.play(); }, 200); }); }); }; initiateHoverDesign(); });
Improved technique to add or remove a CSS class name based on the scroll position form the top of the page
<script> document.addEventListener('DOMContentLoaded', function () { let aboveThisPos = 300; /* Show up after scroling down 300px */ let targetElem = document.querySelector('.StickyNavIcons'); let observerOptions = { threshold: 0, rootMargin: () => { return `${aboveThisPos + 100}px 0px -${aboveThisPos + window.innerHeight}px 0px` }, } const inViewCallback = entries => { entries.forEach((entry) => { if (entry.isIntersecting) { targetElem.classList.add('isShown'); } else { targetElem.classList.remove('isShown'); } }); } let observer = new IntersectionObserver(inViewCallback, {threshold: observerOptions.threshold,rootMargin: observerOptions.rootMargin()}); observer.observe(document.body); let windowWidth = window.innerWidth; window.addEventListener('resize', () => { if (windowWidth === window.innerWidth) return; observer.unobserve(document.body); observer = new IntersectionObserver(inViewCallback, {threshold: observerOptions.threshold,rootMargin: observerOptions.rootMargin()}); observer.observe(document.body); }); }); </script>
Hide Remove 1 px line on gallery WooCommerce that previews the upcoming image
.woocommerce-product-gallery.woocommerce-product-gallery--with-images::before { content: ''!important; position: absolute; height: 100%; width: 100%; top: 0; left: 0; background: linear-gradient( 90deg , rgba(255,255,255,1) 1%, rgba(255,255,255,0) 2%); z-index: 9; pointer-events: none; }
WooCommerce Quantity Field add quantity button control
add_action( 'wp_footer', 'ts_quantity_plus_less' ); function ts_quantity_plus_less() { // To run this only on the single product page if ( ! is_product() ) return; ?> <script> document.addEventListener('DOMContentLoaded', function() { jQuery(function($){ if (!document.querySelector('div.quantity')) { return; } document.querySelector('div.quantity').insertAdjacentHTML('afterbegin', `<button type="button" class="less" >-</button><button type="button" class="plus" >+</button>`) $('form.cart').on('click', 'button.plus, button.less', function () { // Get current quantity values var qty = $(this).closest('form.cart').find('.qty'); var val = parseFloat(qty.val()); var max = parseFloat(qty.attr('max')); var min = parseFloat(qty.attr('min')); var step = parseFloat(qty.attr('step')); // Change the value if plus or less if ($(this).is('.plus')) { if (max && (max <= val)) { qty.val(max); } else { qty.val(val + step); } } else { if (min && (min >= val)) { qty.val(min); } else if (val > 1) { qty.val(val - step); } } }); }); }); </script> <style> /*WooCommerce Add Substract buttons quantity input field */ .quantity.quantity.quantity { position: relative; } .woocommerce .quantity .qty { text-align: left; } button.plus.plus.plus.plus.plus, button.less.less.less.less.less { color: black; font-size: 27px; padding: 0 6px; background-color: transparent; position: absolute; width: 20px; height: 20px; line-height: 0; z-index: 9; font-weight: 300; top: 50%; border: 0; font-weight: 600; /* margin-top: -15px; */ margin: 0; transform: translateY(-60%); } button.plus:hover, button.less:hover, button.plus:focus, button.less:focus { color: black; outline: none; } button.less { left: 20px; } button.plus { right: 20px; } button.plus:active, button.less:active, button.plus:focus, button.less:focus, button.plus:hover, button.less:hover { border-color: #00000040; background-color: transparent; } .product .elementor-jet-single-add-to-cart .cart .quantity input::-webkit-outer-spin-button, .product .elementor-jet-single-add-to-cart .cart .quantity input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .product .elementor-jet-single-add-to-cart .cart .quantity .input-text { -moz-appearance: textfield; /* Firefox */ } </style> <?php }
WP Social Ninja change Google reviews into marquees. Needs to be set to "grid" in "templates"
add_action( 'wp_footer', function(){ if (is_front_page()){ ?> <script> window.addEventListener('load', function () { let listingUL = document.querySelector('.wpsr-all-reviews'); let reviews = Array.from(listingUL.children); let bothHalfReviewsArray = [reviews.slice(0, Math.floor(reviews.length / 2)), reviews.slice(Math.floor(reviews.length / 2))]; let wrapper1, wrapper2; let wrappers = [wrapper1, wrapper2]; wrappers.forEach((wrapper, i) => { wrapper = document.createElement('div'); wrapper.classList.add('marqueeWrapper'); listingUL.appendChild(wrapper); let innerWrapper = document.createElement('div'); innerWrapper.classList.add('marqueeInnerWrapper'); bothHalfReviewsArray[i].forEach(review => innerWrapper.appendChild(review)); wrapper.appendChild(innerWrapper); let innerWrapper2 = innerWrapper.cloneNode(true); wrapper.appendChild(innerWrapper2); }); }); </script> <style> .wpsr-all-reviews.wpsr-row { margin: 0; display: block; } .marqueeWrapper { display: flex; width: 100%; flex-direction: row; flex-wrap: nowrap; } .marqueeInnerWrapper { display: flex; width: fit-content; flex-direction: row; flex-wrap: nowrap; color: #212121; -webkit-animation: animate 140s -140s linear infinite; animation: animate 140s -140s linear infinite; will-change: transform; } .marqueeInnerWrapper.marqueeInnerWrapper > * { width: 400px; max-width: initial; flex: initial; -webkit-box-flex: initial; } .marqueeInnerWrapper:nth-child(2) { -webkit-animation: animate2 140s -70s linear infinite; animation: animate2 140s -70s linear infinite; } .marqueeWrapper:hover .marqueeInnerWrapper { animation-play-state:paused; } .marqueeWrapper:nth-child(2) .marqueeInnerWrapper { animation-direction: reverse; } @-webkit-keyframes animate { 0% { transform: translateX(100%); } 100% { transform: translateX(-100%); } } @keyframes animate { 0% { transform: translateX(100%); } 100% { transform: translateX(-100%); } } @-webkit-keyframes animate2 { 0% { transform: translateX(0); } 100% { transform: translateX(-200%); } } @keyframes animate2 { 0% { transform: translateX(0); } 100% { transform: translateX(-200%); } } </style> <?php } });
Transform reviews from the plugin Google Review Ratings into marquee
add_action( 'wp_footer', function(){ ?> <script> window.addEventListener('load',function(){ let listingUL = document.querySelector('.carousel ul.listing'); let reviews = Array.from(listingUL.children); let wrapper1 = document.createElement('div'); wrapper1.classList.add('marqueeWrapper'); listingUL.appendChild(wrapper1); let innerWrapper = document.createElement('div'); innerWrapper.classList.add('marqueeInnerWrapper'); reviews.forEach(review=> innerWrapper.appendChild(review)); wrapper1.appendChild(innerWrapper); let innerWrapper2 = innerWrapper.cloneNode(true); wrapper1.appendChild(innerWrapper2); }) </script> <style> .marqueeWrapper { display: flex; width: 100%; flex-direction: row; flex-wrap: nowrap; } .marqueeInnerWrapper { display: flex; width: fit-content; flex-direction: row; flex-wrap: nowrap; /* width: auto; */ color: #212121; -webkit-animation: animate 100s -100s linear infinite; animation: animate 100s -100s linear infinite; will-change: transform; } .marqueeInnerWrapper:nth-child(2) { -webkit-animation: animate2 100s -50s linear infinite; animation: animate2 100s -50s linear infinite; } .marqueeWrapper:hover .marqueeInnerWrapper { animation-play-state:paused; } .marqueeInnerWrapper.marqueeInnerWrapper.marqueeInnerWrapper.marqueeInnerWrapper.marqueeInnerWrapper.marqueeInnerWrapper > li { width:560px; max-width:560px; margin-right: 25px; } @-webkit-keyframes animate { 0% { transform: translateX(100%); } 100% { transform: translateX(-100%); } } @keyframes animate { 0% { transform: translateX(100%); } 100% { transform: translateX(-100%); } } @-webkit-keyframes animate2 { 0% { transform: translateX(0); } 100% { transform: translateX(-200%); } } @keyframes animate2 { 0% { transform: translateX(0); } 100% { transform: translateX(-200%); } } </style> <?php });
Improved Elementor form checkbox styling
/* Hide elements visually, but not for screenreaders */ .checkboxform [type=checkbox] { position: absolute !important; clip: rect(1px, 1px, 1px, 1px); padding: 0 !important; border: 0 !important; height: 1px !important; width: 1px !important; overflow: hidden; } /* Add a :before to each label and style this as the checkbox you want to have */ .checkboxform [type=checkbox] + label:before { content: ""; width: 20px; height: 20px; border: 2px solid rgba(0, 0, 0, 1); display: inline-block; margin-right: 6px; } /* Change the styling of the :before when the hidden checkbox is checked, you also can add a background-image */ .checkboxform [type=checkbox]:checked + label:before { background: #000 no-repeat center; background-size: 16px 16px; background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='15' height='15'%3E%3Cpath d='M12 2L5 8 2 5 0 7l5 6 9-9z' fill='white'/%3E%3C/svg%3E"); } /* Show when label and checkbox have focus, to make this keyboard accessible > press spacebar to select the checkbox */ .checkboxform [type=checkbox]:focus + label { color: #20bf6b; }
Append all child nodes of a parent to another element
newParent.append(...oldParent.childNodes);
Get Array of Unique Values from an array using Set and Spread operator
let categoriesArray = []; function getCategoriesArray() { for (let i = 0; i < list.length; i++) { categoriesArray.push(list[i].category); } categoriesArray = [...new Set(categoriesArray)]; }
Projects accordions from listing grid with URL auto scroll from another page
document.addEventListener('DOMContentLoaded', function () { jQuery(function ($) { function switcherGlideSmooth(openers, content) { $(content).hide(); $(openers).css('cursor', 'pointer'); $(openers).attr('tabindex', 0); let contentWasOpened = false; $(openers).each(function (i) { $(this).on('click', function () { let currentOpener = $(this); $(openers).not(this).removeClass('projectIsActive'); currentOpener.toggleClass('projectIsActive'); let openedSection = $(content).eq(i); $(content).not(openedSection).slideUp(); openedSection.slideToggle(); /* scrollToProperPlace(currentOpener); */ currentOpener.hasClass('projectIsActive') ? contentWasOpened = true : contentWasOpened = false; }); }); function scrollToProperPlace(currentOpener) { $('html, body').animate({ scrollTop: currentOpener.offset().top - 90, }, 'slow'); } let projects = document.querySelectorAll(openers); for (let i = 0; i < projects.length; i++){ let linkText = projects[i].querySelector('.elementor-heading-title').textContent.normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/\s+/g, '-').toLowerCase(); if (window.location.href.indexOf(linkText) > -1){ projects[i].click(); scrollToProperPlace($(projects[i])); return; } } } switcherGlideSmooth('.projectCard', '.projectInfo'); }); }); /* on the page that links out to the project page */ document.addEventListener('DOMContentLoaded', function () { let projects = document.querySelectorAll('.jet-listing-grid__item'); projects.forEach(project => { let linkText = project.querySelector('.projectTitle .elementor-heading-title').textContent.normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/\s+/g, '-').toLowerCase(); let card = project.querySelector('.projectCard'); card.href = card.href + "#" + linkText; }); });
Scroll boxes scroling containers with dynamic gradient showing there is more to see, for vertical scrolling containers
<script> document.addEventListener('DOMContentLoaded', function () { let scrollContainers = document.querySelectorAll('.scrollContainer'); scrollContainers.forEach(scrollContainer => { let scrollTop = scrollContainer.scrollTop; let scrollHeight = scrollContainer.scrollHeight; let height = scrollContainer.offsetHeight; updateClassNames() scrollContainer.addEventListener('scroll', function () { updatePosition(); updateClassNames(); }); function updateClassNames() { if (scrollTop < 70) { scrollContainer.classList.add('scrollContainer_atTop'); } else if (scrollTop + height + 70 > scrollHeight) { scrollContainer.classList.add('scrollContainer_atBottom'); } else { scrollContainer.classList.remove('scrollContainer_atTop'); scrollContainer.classList.remove('scrollContainer_atBottom'); } } function updatePosition() { scrollTop = scrollContainer.scrollTop; scrollHeight = scrollContainer.scrollHeight; height = scrollContainer.offsetHeight; } }); }); </script> <style> .scrollContainer { transition: height 0.4s, -webkit-mask-size 0.4s, mask-size 0.4s, -webkit-mask-position 0.4s, mask-position 0.4s; /*change to background instead of mask to see what's going on and adjust */ -webkit-mask-image: linear-gradient(black 0 0), linear-gradient(0deg, rgba(0, 0, 0, 0) 1%, rgba(0, 0, 0, 1) 18%, rgba(0, 0, 0, 1) 81%, rgba(0, 0, 0, 0) 99%); -webkit-mask-position: left center, right center; -webkit-mask-size: 20px 100%, calc(100% - 20px) 100%; -webkit-mask-repeat: no-repeat; mask: linear-gradient(black 0 0) left, linear-gradient(0deg, rgba(0, 0, 0, 0) 1%, rgba(0, 0, 0, 1) 18%, rgba(0, 0, 0, 1) 81%, rgba(0, 0, 0, 0) 99%) right; mask-position: left center, right center; mask-size: 20px 100%, calc(100% - 20px) 100%; mask-repeat: no-repeat; } .scrollContainer.scrollContainer_atTop { -webkit-mask-size: 20px 100%, calc(100% - 20px) 140%; -webkit-mask-position: left bottom, right bottom; mask-size: 20px 100%, calc(100% - 20px) 140%; mask-position: left bottom, right bottom; } .scrollContainer.scrollContainer_atBottom { -webkit-mask-size: 20px 100%, calc(100% - 20px) 140%; -webkit-mask-position: left top, right top; mask-size: 20px 100%, calc(100% - 20px) 140%; mask-position: left top, right top; } </style>
Make jet listing grid horizontally scrollable netflix
<style> .jet-listing-grid__item.jet-listing-grid__item { flex: 0 0 auto; flex-shrink: 0; margin: 10px; width: 47%; } .jet-listing-grid__items.jet-listing-grid__items { display: flex; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; cursor: grab; /* Change to unset to have the normal cursor */ } .jet-listing-grid__items::-webkit-scrollbar { height: 14px; /* Change to 6px to make the scrollbar smaller, or to 0 to remove it */ } /* Color of scrollbar track */ .jet-listing-grid__items::-webkit-scrollbar-track { background: rgb(255 255 255 / 30%); } /* Color of scrollbar thumb */ .jet-listing-grid__items::-webkit-scrollbar-thumb { background: rgb(255 255 255 / 50%); } .jet-listing-grid__items.active { cursor: grabbing; /* Change to unset to have the normal cursor when mouse button is clicked down */ cursor: -webkit-grabbing; /* idem */ } /* Firefox scroll bar styling */ .jet-listing-grid__items { scrollbar-color: rgb(255 255 255 / 50%) rgb(255 255 255 / 30%); scroll-bar-width: thin; } /* Prevents clicking on links while navigating left or right */ .noclick { pointer-events: none; } </style> <script> document.addEventListener("DOMContentLoaded", function (event) { const horizons = document.querySelectorAll('.jet-listing-grid__items'); let isDown = false; let startX; let scrollLeft; const leftArrow = document.querySelectorAll('.leftarrow'); const rightArrow = document.querySelectorAll('.rightarrow'); horizons.forEach((horizon, i) => { let scrollAmount; scrollAmount = horizon.offsetWidth; /* Change this to 300 to scroll by 300 pixels on each arrow click, for example. As set, it scrolls the width of the elements shown */ window.addEventListener('resize', function () { scrollAmount = horizon.offsetWidth; /* idem , needs to be same value as above */ }); let smoothFunction = function () { horizon.setAttribute('style', 'scroll-behavior:smooth;') }; let clearBehavior = function () { setTimeout(function () { horizon.removeAttribute('style'); }, 771) }; if (leftArrow[i]) { leftArrow[i].setAttribute('tabindex', '0'); rightArrow[i].setAttribute('tabindex', '0'); leftArrow[i].onclick = function () { smoothFunction(); horizon.scrollLeft -= scrollAmount; clearBehavior(); }; rightArrow[i].onclick = function () { smoothFunction(); horizon.scrollLeft += scrollAmount; clearBehavior(); }; } const links = horizon.querySelectorAll('.jet-listing-grid__item'); horizon.addEventListener('mousedown', (e) => { isDown = true; horizon.classList.add('active'); startX = e.pageX - horizon.offsetLeft; scrollLeft = horizon.scrollLeft; }); horizon.addEventListener('mouseleave', () => { isDown = false; horizon.classList.remove('active'); for (var i = 0; i < links.length; i++) { links[i].classList.remove('noclick'); } }); horizon.addEventListener('mouseup', () => { isDown = false; horizon.classList.remove('active'); for (var i = 0; i < links.length; i++) { links[i].classList.remove('noclick'); } }); horizon.addEventListener('mousemove', (e) => { if (!isDown) return; e.preventDefault(); const x = e.pageX - horizon.offsetLeft; const walk = (x - startX) * 1.6; horizon.scrollLeft = scrollLeft - walk; for (var i = 0; i < links.length; i++) { links[i].classList.add('noclick'); } }); }); }); </script>
Remove all newline and tabs and returns \n \r \t characters REGEX
string.replace(/\r|\n|\t/g, '');
Elementor Gallery Pro Change load animation to fade only
.elementor-gallery-item__image.e-gallery-image { transition-property: opacity; transition-duration: 1.3s; } .e-gallery-image.elementor-gallery-item__image { opacity:0; } .e-gallery-image.elementor-gallery-item__image.e-gallery-image-loaded { opacity:1; }
Play videos when in viewport, pause otherwise
const videos = document.querySelectorAll("video"); videos.forEach(video => { let playState = null; const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (!entry.isIntersecting) { video.pause(); playState = false; } else { video.play(); playState = true; } }); }, {}); observer.observe(video); const onVisibilityChange = () => { if (document.hidden || !playState) { video.pause(); } else { video.play(); } }; document.addEventListener("visibilitychange", onVisibilityChange); });
Replace swiper pagination with 01 02 03 04 with a line above, customize "Reviews Slider" element
selector .elementor-testimonial { display: flex; flex-direction: column-reverse; } selector .elementor-testimonial__content { height: auto; } selector .swiper-pagination.swiper-pagination-clickable { text-align: left; left: 40px; width: calc(100% - 40px); } selector .swiper-pagination.swiper-pagination-clickable > span { width: 130px; height: auto; background-color: transparent; opacity: 0.6; color: #000; position: relative; margin: 0; } selector .swiper-pagination.swiper-pagination-clickable > .swiper-pagination-bullet-active { opacity: 1; } selector .swiper-pagination.swiper-pagination-clickable > span::after { content:''; position: absolute; width: 100%; height: 1px; left: 0; top: -3px; background-color: #333; } selector .swiper-pagination.swiper-pagination-clickable > span:nth-child(1)::before { content:'01'; } selector .swiper-pagination.swiper-pagination-clickable > span:nth-child(2)::before { content:'02'; } selector .swiper-pagination.swiper-pagination-clickable > span:nth-child(3)::before { content:'03'; } selector .swiper-pagination.swiper-pagination-clickable > span:nth-child(4)::before { content:'04'; }
Limit carousel lightboxes to opening only the images within the carousel or galleries
/* * Copyright 2022 Maxime Desrosiers */ function wp_fix_lightbox_for_dynamic_carousels_and_galleries() { ?> <script> window.addEventListener('load', function () { /* Limit carousel lightboxes to opening only the images within the carousel or galleries (instead of all the carousels on the page) */ let carouselsAndGalleries = document.querySelectorAll('.elementor-image-gallery, .elementor-image-carousel-wrapper'); /* excludes gallery pro for now, add when needed */ carouselsAndGalleries.forEach((e, i) => { e.querySelectorAll('a').forEach(anchor => anchor.setAttribute('data-elementor-lightbox-slideshow', '34532' + i)); }); }); </script> <?php } add_action( 'wp_footer', 'wp_fix_lightbox_for_dynamic_carousels_and_galleries');
Play videos automatically on switching tabs, pause all others
document.addEventListener('DOMContentLoaded', function () { window.setTimeout(function(){ function manageVideos() { let tabTitles = document.querySelectorAll('.videos-tabs .eael-tabs-nav li'); let activeTab = document.querySelector('.videos-tabs .eael-tabs-nav .active'); let activeTabIndex = +document.querySelector('.videos-tabs .eael-tabs-nav .active').getAttribute('data-tab') - 1; let formInputField = document.getElementById('input_1_2'); formInputField.value = activeTabIndex + 1; managePlayback(activeTab, activeTabIndex); tabTitles.forEach( (tabTitle, i) => { tabTitle.addEventListener('click', function () { activeTabIndex = i; formInputField.value = i + 1; managePlayback(tabTitle, activeTabIndex); }); }); }; function managePlayback(tabTitle, activeTabIndex) { let allVideos = document.querySelectorAll('.videos-tabs iframe[allowfullscreen],.videos-tabs video'); let videoPlayers = Array.from(allVideos).map(v => v.tagName == 'IFRAME' ? new Vimeo.Player(v) : v); videoPlayers.forEach((player, i) => { player.pause(); player.volume = 0; player.setVolume ? player.setVolume(0) : false; }); let contentTabs = document.querySelectorAll('.videos-tabs .eael-tab-content-item')[activeTabIndex]; let currentVideos = contentTabs.querySelectorAll('iframe[allowfullscreen], video'); let currentVideoPlayers = Array.from(currentVideos).map(v => v.tagName == 'IFRAME' ? new Vimeo.Player(v) : v); currentVideoPlayers.forEach((player, i) => { player.play(); player.volume = 1; player.setVolume ? player.setVolume(1) : false; }); }; manageVideos(); },1300); });
Run netflix like horizontal scroll in a popup
document.addEventListener("DOMContentLoaded", function (event) { let isDown = false; let startX; let scrollLeft; function init(isPopup) { let horizons; let leftArrow; let rightArrow; if (isPopup) { horizons = document.querySelectorAll('.dialog-lightbox-widget .horizontale'); leftArrow = document.querySelectorAll('.dialog-lightbox-widget .leftarrow'); rightArrow = document.querySelectorAll('.dialog-lightbox-widget .rightarrow'); } else { horizons = document.querySelectorAll('.horizontale:not(.horizontal-init)'); leftArrow = document.querySelectorAll('.leftarrow:not(.horizontal-init)'); rightArrow = document.querySelectorAll('.rightarrow:not(.horizontal-init)'); } horizons.forEach((horizon, i) => { horizon.classList.add('horizontal-init'); leftArrow[i].classList.add('horizontal-init'); rightArrow[i].classList.add('horizontal-init'); let scrollAmount; if (horizon.querySelector('.elementor-posts-container')) { let scrollPostContainerWidth = horizon.querySelector('.elementor-widget-container').offsetWidth; let scrollPostsAmount = horizon.querySelectorAll('article').length; let scrollPostWidth = scrollPostContainerWidth / scrollPostsAmount; scrollAmount = scrollPostWidth; } else { scrollAmount = horizon.offsetWidth; /* Change this to 300 to scroll by 300 pixels on each arrow click, for example. As set, it scrolls the width of the elements shown */ window.addEventListener('resize', function () { scrollAmount = horizon.offsetWidth; /* idem , needs to be same value as above */ }); } function smoothFunction() { horizon.style.scrollBehavior = 'smooth'; } function clearBehavior() { setTimeout(function () { horizon.style.removeProperty('scroll-behavior'); }, 771); } if (leftArrow[i]) { leftArrow[i].setAttribute('tabindex', '0'); rightArrow[i].setAttribute('tabindex', '0'); leftArrow[i].onclick = function () { smoothFunction(); horizon.scrollLeft -= scrollAmount; clearBehavior(); }; rightArrow[i].onclick = function () { smoothFunction(); horizon.scrollLeft += scrollAmount; clearBehavior(); }; } const links = horizon.querySelectorAll('.horizontale > div'); horizon.addEventListener('mousedown', (e) => { isDown = true; horizon.classList.add('active'); startX = e.pageX - horizon.offsetLeft; scrollLeft = horizon.scrollLeft; }); horizon.addEventListener('mouseleave', () => { isDown = false; horizon.classList.remove('active'); for (var i = 0; i < links.length; i++) { links[i].classList.remove('noclick'); } }); horizon.addEventListener('mouseup', () => { isDown = false; horizon.classList.remove('active'); for (var i = 0; i < links.length; i++) { links[i].classList.remove('noclick'); } }); horizon.addEventListener('mousemove', (e) => { if (!isDown) return; e.preventDefault(); const x = e.pageX - horizon.offsetLeft; const walk = (x - startX) * 1.6; horizon.scrollLeft = scrollLeft - walk; for (var i = 0; i < links.length; i++) { links[i].classList.add('noclick'); } }); }); } init(); if (typeof jQuery === 'function') { jQuery(document).on('elementor/popup/show', () => { init(true); }); } });
HeaderShowScrollBackUp__EnoughDistanceOnly
<script> document.addEventListener('DOMContentLoaded', function () { jQuery(function ($) { let startsFromTop = 150; /* starting point from top of page */ let showOnScrollBackUp = 200; /* show on scroll back up 200px */ let mywindow = $(window); let mypos = mywindow.scrollTop(); let lowPointPosition = mywindow.scrollTop(); let scrolling = false; /* For throlling scroll event */ window.addEventListener('scroll', function () { scrolling = true; }); setInterval(() => { if (scrolling) { scrolling = false; if (mypos > startsFromTop) { if (mywindow.scrollTop() > mypos) { $('#headervoll').addClass('headerup'); } else if (mywindow.scrollTop() + showOnScrollBackUp < lowPointPosition) { $('#headervoll').removeClass('headerup'); lowPointPosition = mywindow.scrollTop(); } } if (mywindow.scrollTop() > mypos || mywindow.scrollTop() + showOnScrollBackUp < lowPointPosition){ lowPointPosition = mywindow.scrollTop(); } console.log(lowPointPosition); mypos = mywindow.scrollTop(); } if (mywindow.scrollTop() < startsFromTop) { $('#headervoll').removeClass('headerup'); } }, 300); }); }); </script> <style> #headervoll{ transition : transform 2.0s ease; } .headerup{ transform: translateY(-400px); /*adjust this value to the height of your header*/ } </style>
Jet Forms add file name
<script> let fileUploads = document.querySelectorAll('.jet-form-builder-file-upload'); fileUploads.forEach(fileUpload => { let select = fileUpload.querySelector('.jet-form-builder-file-upload__input'); let label = fileUpload.parentElement.querySelector('label'); let star = label.querySelector('.jet-form-builder__required'); let fileNameWrapper = document.createElement('div'); fileNameWrapper.classList.add('file-name-wrapper') star.insertAdjacentElement('afterEnd', fileNameWrapper); select.addEventListener('change', function () { setTimeout(function () { let fileName = fileUpload.querySelector('.jet-form-builder-file-upload__file-remove').dataset.fileName; fileNameWrapper.innerHTML = fileName; }, 900); }); }); </script> <style> label[for="cv"], label[for="lettre"] { width: 100%; padding: 8px 0; position: relative; cursor: pointer; } label[for="cv"]::after, label[for="lettre"]::after { content: "+"; font-size: 39px; position: absolute; right: 10px; top: -4px; } .jet-form-builder__field-wrap.jet-form-builder-file-upload { display: none; } .file-name-wrapper { font-weight:700; margin-top:10px; } </style> /* https://rosefellow.devsynergie.com/carrieres/ */
Remove scrolling / overflow on the page when any Elementor popup is opened
window.addEventListener("load", function () { setTimeout(() => { jQuery(document).on('elementor/popup/hide', (event, id, instance) => { document.documentElement.style.removeProperty('overflow'); }); jQuery(document).on('elementor/popup/show', (event, id, instance) => { document.documentElement.style.overflow = "hidden"; }); }, 1400); });
Image reveal on entering viewport
<script> document.addEventListener('DOMContentLoaded', function () { let imageScale = document.querySelectorAll('.image_intro'); const options = { threshold: 0, rootMargin: '-70% 0px -29%', } const inViewCallback = entries => { entries.forEach((entry, i, arr) => { let elem = entry.target; if (entry.isIntersecting) { elem.classList.add('intro'); } }); } let observer = new IntersectionObserver(inViewCallback, options); imageScale.forEach(element => { observer.observe(element); }); }); </script> <style> .image_intro{ -webkit-clip-path: polygon(12% 0%, 88% 0%, 88% 100%, 12% 100%); clip-path: polygon(12% 0%, 88% 0%, 88% 100%, 12% 100%); -webkit-transition: -webkit-clip-path 1.23s cubic-bezier(.23,1,.32,1); transition: -webkit-clip-path 1.23s cubic-bezier(.23,1,.32,1); -o-transition: clip-path 1.23s cubic-bezier(.23,1,.32,1); transition: clip-path 1.23s cubic-bezier(.23,1,.32,1); transition: clip-path 1.23s cubic-bezier(.23,1,.32,1), -webkit-clip-path 1.23s cubic-bezier(.23,1,.32,1); } .image_intro.intro{ -webkit-clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%); } </style>
Fallback JS for single click on nav toggle when optimized, exclude this JS
(function () { if (!window.elementorProFrontend) { let navToggles = document.querySelectorAll('.elementor-menu-toggle'); let nav = document.querySelector('nav.elementor-nav-menu--dropdown'); let hasRun = false; let ElementorNavFallBackFunction = function (navToggle) { return function curried_func(e) { if (hasRun) { return; } hasRun = true; setTimeout(() => { if (!navToggle.classList.contains('elementor-active')) { navToggle.classList.add('elementor-active'); navToggle.setAttribute('aria-expanded', 'true'); nav.style.width = '100vw'; nav.style.position = 'fixed'; nav.style.left = '0'; nav.style.top = '58px'; nav.setAttribute('aria-hidden', 'false'); } }, 200); navToggle.removeEventListener('click', curried_func); } } navToggles.forEach(navToggle => { navToggle.addEventListener('click', ElementorNavFallBackFunction(navToggle)); }) } }());
Elementor disable page scrolling when popup is opened
add_action( 'wp_footer', function () { ?> <script> window.addEventListener("load", function () { jQuery(document).on('elementor/popup/hide', (event, id, instance) => { document.documentElement.style.removeProperty('overflow'); }); jQuery(document).on('elementor/popup/show', (event, id, instance) => { document.documentElement.style.overflow = "hidden"; }); }); </script> <?php } );
Centered map from image on mobile in a horizontal scroll div, with overlay to let user know to scroll horizontally, and start automaticallly scrolled to the center of the map horizontally
(function () { let mapImage = document.querySelector('.mapImage >.elementor-widget'); let mapImageWrapper = mapImage.parentElement; let scrollAmount = mapImage.scrollWidth / 2 - window.innerWidth / 2; mapImage.scrollTo(scrollAmount, 0); mapImageWrapper.addEventListener('click', function () { mapImageWrapper.classList.add('mapImage__ready'); }) mapImage.addEventListener('scroll', removeOverlay); let eventFired = 0; function removeOverlay() { if (eventFired > 3) { mapImageWrapper.classList.add('mapImage__ready'); mapImage.removeEventListener('scroll', removeOverlay); } eventFired++ } }());
Sort containers that have posts from different categories by those with the most posts, add or remove see more button if needed or not
document.addEventListener('DOMContentLoaded', function () { let categories = Array.from(document.querySelectorAll('.eventCategory')); let parentWrapper = document.querySelector('.eventCategoriesParent'); let listItemsQuantity = []; categories.forEach(category => { let listItems = category.querySelectorAll('.elementor-post'); let seeMoreButton = category.querySelector('.eventSeeMore'); if (listItems.length < 1) { category.style.display = 'none'; } if (listItems.length < 4) { seeMoreButton.style.display = 'none'; } listItemsQuantity.push(listItems.length); }); categories.sort((a, b) => listItemsQuantity.indexOf(categories.indexOf(a)) - listItemsQuantity.indexOf(categories.indexOf(b))); categories.forEach(category => parentWrapper.appendChild(category)); });
Hover lotties play segments specific mouse in mouse out, for Elementor Nav Menu element
<script> window.addEventListener('load', function () { let lottieFilesUrl = ['https://richardw140.sg-host.com/wp-content/uploads/2022/04/Projects-1.json', 'https://richardw140.sg-host.com/wp-content/uploads/2022/04/About-1.json', 'https://richardw140.sg-host.com/wp-content/uploads/2022/04/Studio-1.json', 'https://richardw140.sg-host.com/wp-content/uploads/2022/04/Contact-1.json']; let popupNavItems = document.querySelectorAll('.jet-popup .elementor-nav-menu li'); popupNavItems.forEach((navItem, i) => { let lottieWrapper = document.createElement('div'); lottieWrapper.classList.add('nav-lotties'); navItem.appendChild(lottieWrapper); let animData = { container: lottieWrapper, path: lottieFilesUrl[i], renderer: 'svg', loop: false, autoplay: false, name: "animScroll", }; let animScroll = bodymovin.loadAnimation(animData); navItem.addEventListener('mouseenter', function () { animScroll.playSegments([0, 12], true); }); navItem.addEventListener('mouseleave', function () { animScroll.playSegments([12, 24], true); }); }); }); </script> <style> .jet-popup .elementor-nav-menu li{ position:relative; } .jet-popup .elementor-nav-menu li .nav-lotties { position: absolute; pointer-events: none; top:0; left:-20px; } </style>
Check if all checkboxes are checked, then that
document.addEventListener('DOMContentLoaded', function() { let tabContents = document.querySelectorAll('.nav_tabContent'); tabContents.forEach(tabContent => { let checkboxes = Array.from(tabContent.querySelectorAll('input[type="checkbox"]')); let button = tabContent.querySelector('.orderButton') checkboxes.forEach(checkbox => { checkbox.addEventListener('change',function(){ if (checkboxes.every(checkbox => checkbox.checked)){ button.classList.add('orderButton__enabled'); } else { button.classList.remove('orderButton__enabled'); } }); }) }); });
Elementor fade in tabs content
.elementor-tab-content { animation: fadeInTabs 0.8s ease forwards; } @keyframes fadeInTabs{ 0% { opacity:0; } 100% { opacity:1; } }
Popup from custom loop sections or containers (as done for Tim, and for sf with agency team)
<div class='popupSection_arrowNav popupSection_leftArrow'> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z"/></svg></div> <div class='popupSection_arrowNav popupSection_rightArrow'> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><!-- Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"/></svg></div> <a href="#close-popup" class="popupSection_closePopup"></a> <script> /* Popups on click code */ document.addEventListener('DOMContentLoaded', function() { let showPopups = document.querySelectorAll('.popupSection_triggerPopup'); let popups = document.querySelectorAll('.popupSection'); let closePopups = document.querySelectorAll('.popupSection, .popupSection > div, .popupSection_closePopup'); let closePopupButton = document.querySelector('.popupSection_closePopup'); let leftarrow = document.querySelector('.popupSection_leftArrow'); let rightarrow = document.querySelector('.popupSection_rightArrow'); let zindex = 9144; let zindexReset = zindex; let onPageLoad = true; let index = 0; function managePopupEvents() { setElements(); showPopups.forEach((e, i) => { if (e.classList.contains('popupSection_ready')) return; e.classList.add('popupSection_ready'); setShowPopupsListeners(e, i); }); closePopups.forEach((e, i) => { if (e.classList.contains('popupSection_closePopups_ready')) return; e.classList.add('popupSection_closePopups_ready'); setClosePopupsListeners(e); }); }; managePopupEvents(); function setShowPopupsListeners(e, i) { e.addEventListener('click', function() { popups[i].style.display = 'block'; setTimeout(function() { popups.forEach(e => e.style.opacity = '1'); }, 14); document.body.style.overflow = 'hidden'; leftarrow.style.display = 'block'; rightarrow.style.display = 'block'; closePopupButton.style.display = 'block'; index = i; }); if (onPageLoad) { manageURL(e, i) } }; leftarrow.addEventListener('click', function() { index = manageLeftArrowClick(index, popups); }); rightarrow.addEventListener('click', function() { index = manageRightArrowClick(index, popups); }); /* Load the right popup on page load, if name is in the URL */ function manageURL(e, i) { let positionName = e.querySelector('.popupSection_positionName h2').textContent.replace(/\s+/g, '-').toLowerCase().replace(/[^a-z0-9\-]/g, ''); positionName = '#' + positionName; console.log(positionName); if (window.location.href.toLowerCase().indexOf(positionName) > -1) { jQuery('.popupSection_triggerPopup').eq(i).click(); } } function setElements() { showPopups = document.querySelectorAll('.popupSection_triggerPopup'); popups = document.querySelectorAll('.popupSection'); closePopups = document.querySelectorAll('.popupSection, .popupSection > div, .popupSection_closePopup'); }; function setClosePopupsListeners(e) { e.addEventListener('click', function(event) { if (event.target !== e) return; popups.forEach(e => { e.style.display = 'none'; e.style.zIndex = zindexReset; e.style.opacity = '0'; }); document.body.style.removeProperty('overflow'); leftarrow.style.display = 'none'; rightarrow.style.display = 'none'; closePopupButton.style.display = 'none'; zindex = zindexReset; }); } function manageLeftArrowClick(index, popups) { if (index !== 0) { index--; } else { index = popups.length - 1; } popups.forEach(e => e.style.display = 'none'); popups[index].style.display = 'block'; popups[index].style.zIndex = ++zindex; return index; } function manageRightArrowClick(index, popups) { if (index !== popups.length - 1) { index++; } else { index = 0; } popups.forEach(e => e.style.display = 'none'); popups[index].style.display = 'block'; popups[index].style.zIndex = ++zindex; return index; } /* Compatible with DOM mutations (aka AJAX filtering) */ function setDomMutationObserver() { const targetNode = document.querySelector('.popupSection_listing') const config = { attributes: true, subtree: true }; const callback = function(mutationsList, observer) { onPageLoad = false; managePopupEvents(); }; const observer = new MutationObserver(callback); observer.observe(targetNode, config); }; setDomMutationObserver(); }); </script> <style> .popupSection_triggerPopup, .popupSection_closePopup { cursor: pointer; } .popupSection.popupSection.popupSection.popupSection { display: none; position: fixed; width: 100%; height: 100%; top: 0; left: 0; z-index: 9144; background: #000000be; overflow-y: scroll; opacity: 0; transition: opacity 0.3s linear; } .popupSection.popupSection.popupSection.popupSection { height: 100%; } .popupSection>div { padding: 90px 30px; } .popupSection::-webkit-scrollbar { width: 0; } .popupSection_arrowNav svg { z-index: 99999; width: 46px; fill: #fff; } @media (max-width:1230px) { .popupSection_arrowNav svg { display: none; } } .popupSection_arrowNav { position: fixed; top: 50%; transform: translateY(-50%); left: 4%; color: #fff; font-size: 33px; cursor: pointer; z-index: 99999; display: none; } .popupSection_arrowNav.popupSection_rightArrow { left: unset; right: 4%; } .popupSection_closePopup { position: fixed; right: 32px; top: 32px; width: 32px; height: 32px; opacity: 0.8; z-index: 99999; display: none; } .popupSection_closePopup:hover { opacity: 1; } .popupSection_closePopup:before, .popupSection_closePopup:after { position: absolute; left: 15px; content: ' '; height: 33px; width: 3px; background-color: #fff; } .popupSection_closePopup:before { transform: rotate(45deg); } .popupSection_closePopup:after { transform: rotate(-45deg); } </style>
Scroll snap CSS doesn't play well with background-attachment:fixed;
Scroll snap CSS doesn't play well with background-attachment:fixed; on chrome at least it will prevent fast snapping when enabling Always option
Straight vanilla Javascript scroll image sequence
<canvas id="hero-lightpass"></canvas> <script> /* .stickyparent is the parent section, .stickyparent > .elementor-container is the inner section that is sticky */ let parentPos, childPos, relativePos, totalHeight, scrollFromTop, scrollRatio; document.addEventListener('DOMContentLoaded', function () { let stickyParent = document.querySelector('.stickyparent'); let stickySection = document.querySelector('.stickyparent > .elementor-container'); /* adapted from https://codepen.io/j-v-w/pen/ZEbGzyv?editors=1010 */ const html = document.documentElement; const canvas = document.getElementById("hero-lightpass"); const context = canvas.getContext("2d"); const frameCount = 9; const currentFrame = index => ( `http://jby.7b7.myftpupload.com/wp-content/uploads/2022/09/Image-${index.toString()}.jpg` ) const preloadImages = () => { for (let i = 1; i < frameCount; i++) { const img = new Image(); img.src = currentFrame(i); } }; const img = new Image() img.src = currentFrame(1); canvas.width = 524; canvas.height = 824; img.onload = function () { context.drawImage(img, 0, 0); } const updateImage = index => { img.src = currentFrame(index); context.drawImage(img, 0, 0); console.log('updateImage function has run'); } window.addEventListener('scroll', () => { parentPos = stickyParent.getBoundingClientRect(); childPos = stickySection.getBoundingClientRect(); scrollFromTop = childPos.top - parentPos.top; totalHeight = stickyParent.offsetHeight - (stickySection.offsetHeight); scrollRatio = scrollFromTop / totalHeight; let frameIndex = Math.min( frameCount - 1, Math.ceil(scrollRatio * frameCount) ); /* recycling the first frame */ if (frameIndex == 8){ frameIndex = 0; } requestAnimationFrame(() => updateImage(frameIndex + 1)) }); preloadImages(); }); </script> <style> .stickyparent { min-height: 270vh; } .stickyparent.stickyparent.stickyparent.stickyparent > .elementor-container{ position: sticky; top: 0; height: 100vh; } #hero-lightpass { width: auto; height: min(90vh, 824px); } </style>
Autoplaying smooth Swiper with good UX, works with Elementor basic image gallery as it is
<style> :root { --swiper-height: 300px; --swiper-height-mobile: 300px; } body.elementor-editor-active .carouselsection{ overflow: hidden!important; } .justified_gallery .swiper-wrapper, body:not(.elementor-editor-active) .justified_gallery .gallery { height: var(--swiper-height, 300px); } .justified_gallery .swiper-slide img { height: var(--swiper-height); width: auto; } .justified_gallery .elementor-image-gallery .gallery-item { width: auto; max-width: unset; } @media (max-width:767px) { .justified_gallery .swiper-wrapper, body:not(.elementor-editor-active) .justified_gallery .gallery { height: var(--swiper-height-mobile, 300px); } .justified_gallery .swiper-slide img { height: var(--swiper-height-mobile, 300px); } } .elementor-editor-active .ready { display: none; } </style> <script src="https://unpkg.com/swiper/swiper-bundle.min.js"></script> <script> document.addEventListener('DOMContentLoaded', function() { if (jQuery('#gallery-1')) { jQuery('#gallery-1').attr('id', ''); } }); (function(){ if (document.querySelector('.elementor-editor-active')){ return; } let parent = document.querySelector('.justified_gallery .gallery'); let children = parent.querySelectorAll('.justified_gallery .gallery-item'); let wrapper = document.createElement('div'); wrapper.className = 'swiper-wrapper'; children.forEach((childe) => { wrapper.appendChild(childe); childe.classList.add('swiper-slide'); }); let container = document.createElement('div'); container.className = 'swiper-containere'; container.prepend(wrapper) parent.prepend(container); var swipere = new Swiper('.swiper-containere', { loop: true, slidesPerView: 'auto', spaceBetween: 30, slideToClickedSlide :true, autoplay: { delay: 1, disableOnInteraction: false }, freeMode: { enabled: true, momemtum: false, }, speed: 20000, }); container.addEventListener('click',function(){ setTimeout(function(){ swipere.slideToLoop(swipere.realIndex + 1); },10); }); }()); </script>
Make videos in a single post template "post content" element 16:9 Such as youtube videos
.elementor .video-container { position: relative; width: 100%; padding-top: 56.25%; } .elementor .video-container iframe { position: absolute; width: 100%; height: 100%; top: 0; left: 0; }
JetSmartFilters to select2
<script> /* Jet Smart Filters Select filter to select2 */ /* Required Select2 CSS and JS are enqueued in a Code Snippet */ window.addEventListener('load', function() { jQuery('.jet-select__control').select2({ minimumResultsForSearch:99, dropdownParent: jQuery(".jet-select__control")[0].parentElement, }); }); </script>
Mobile section, add and remove background color on scroll without using Elementor Sticky feature
#mobile-nav { position: fixed; top: 0; left: 0; width: 100vw; transition: background-color 1s ease; } #wpadminbar #mobile-nav { top:32px; } #mobile-nav.after-scroll { background-color: rgba(0,0,0,0.80) } /* Mobile section, add and remove background color on scroll without using Elementor Sticky feature */ let mobileNav = document.getElementById('mobile-nav'); let mypos = window.pageYOffset || document.documentElement.scrollTop; let scrolling = false; /* For throlling scroll event */ window.addEventListener('scroll', function() { scrolling = true; }); setInterval(() => { if (scrolling) { scrolling = false; mypos = window.pageYOffset || document.documentElement.scrollTop; if (mypos > 130) { mobileNav.classList.add('after-scroll'); } else { mobileNav.classList.remove('after-scroll'); } } }, 200);
Dynamic popup on mobile based on information available in the DOM, for mobile and tablet
<script> (function(){ if (document.querySelector('.elementor-editor-active')){ return; } let popupContainer = document.createElement('div'); let popupContentWrapper = document.createElement('div'); let popupImage = document.createElement('img'); let popupTitle = document.createElement('h3'); let popupText = document.createElement('p'); let popupClose = document.createElement('i'); popupContentWrapper.appendChild(popupImage); popupContentWrapper.appendChild(popupTitle); popupContentWrapper.appendChild(popupText); popupContentWrapper.appendChild(popupClose); popupClose.classList.add('eicon-close'); popupContainer.classList.add('teamBiosPopup'); popupContentWrapper.classList.add('teamBiosPopup_wrapper'); popupImage.classList.add('teamBiosPopup_image'); popupTitle.classList.add('teamBiosPopup_title'); popupText.classList.add('teamBiosPopup_text'); popupClose.classList.add('teamBiosPopup_close'); popupContainer.appendChild(popupContentWrapper); document.body.appendChild(popupContainer); let readMores = document.querySelectorAll('.uc_more_btn'); readMores.forEach( (readMore,i) => { readMore.removeAttribute('href'); readMore.addEventListener('click',function(){ let moreInfoColumn = readMore.closest('.elementor-column').nextElementSibling; popupImage.src = ''; popupImage.src = readMore.closest('.ue-item-holder').querySelector('img').getAttribute('src'); popupTitle.textContent = moreInfoColumn.querySelector('.ue_title').textContent; popupText.innerHTML = moreInfoColumn.querySelector('.ue-text p').innerHTML; document.documentElement.style.overflow = 'hidden'; popupContentWrapper.scrollTo(0,0); popupContainer.classList.add('teamBiosPopup__visible'); }); }); popupClose.addEventListener('click', function(){ popupContainer.classList.remove('teamBiosPopup__visible'); document.documentElement.style.removeProperty('overflow'); }); popupContainer.addEventListener('click', function(event){ if (event.target.classList.contains('teamBiosPopup')){ popupContainer.classList.remove('teamBiosPopup__visible'); document.documentElement.style.removeProperty('overflow'); } }); }()); </script> <style> html,body { overflow-x: hidden; } .teamBiosPopup { background: #000000de; position: fixed; left: 0; top: 0; height: 100%; width: 100vw; z-index: 999; pointer-events: none; opacity: 0; transition: opacity 0.3s; display: flex; flex-direction: column; justify-content: center; align-items: center; } .teamBiosPopup.teamBiosPopup__visible { pointer-events: all; opacity: 1; } .teamBiosPopup_wrapper { display: flex; flex-direction: column; background: #ffffff; padding: 0 20px; overflow-y: auto; height: 80%; position: relative; } .teamBiosPopup_close { position: sticky; bottom: 10px; align-self: flex-end; } .teamBiosPopup_image { margin: 20px 0; } .teamBiosPopup_title { margin-bottom: 20px; } @media (max-width: 767px) { .bio_carousel { display: none; } } @media (min-width: 768px) { .ue-content-bottom { display: none; } } </style>
get straight text content of multiple divs without /n /t etc
element.textContent.toLowerCase().replace(/\r|\n|\t/g, '');
Elementor Class Names Hover Animation (from Frank Tielemans )
Give the column the CSS class elementor-animation-grow and it will scale up on hover. This class is a button hover style, but it works on all elements, but not all elements have the smooth transition. (eg sections don't have the transition) Other valid animation classes are: elementor-animation-pulse elementor-animation-pulse-grow elementor-animation-hang-sink elementor-animation-skew elementor-animation-hang elementor-animation-bob elementor-animation-sink elementor-animation-float elementor-animation-grow-rotate elementor-animation-rotate elementor-animation-bounce-out elementor-animation-bounce-in elementor-animation-pop elementor-animation-push elementor-animation-shrink elementor-animation-buzz-out elementor-animation-buzz elementor-animation-wobble-skew elementor-animation-wobble-bottom elementor-animation-wobble-top elementor-animation-wobble-to-bottom-right elementor-animation-wobble-vertical
CSS and JavaScript for Apple iOS mobile 100vh viewport view
Custom CSS for section: height: 100vh; /* fallback */ height: calc(var(--vh, 1vh) * 100); Custom JS to put into code block before section: jQuery(document).ready( function() { let vh = window.innerHeight * 0.01; document.documentElement.style.setProperty('--vh', `${vh}px`); });
Prevent form submit (like search form with AJAX)
jQuery(".page-id-16736 form").submit(function(e){ e.preventDefault( ); });
Elementor flip box stacking order fix z index problems fix
/* fix stacking order problems when 3d is enabled */ .elementor-flip-box--3d .elementor-flip-box.elementor-flip-box { -webkit-transform-style: initial; transform-style: initial; } .elementor-flip-box--3d .elementor-flip-box.elementor-flip-box:hover { -webkit-transform-style: preserve-3d; transform-style: preserve-3d; }
GSAP scroll trigger text reveal on scroll
/* Works with Elementor Heading elements, with class name "text_reveal_on_scroll" */ function setup_text_reveal_on_scroll() { ?> <script src="https://unpkg.com/split-type"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/gsap.min.js" integrity="sha512-VEBjfxWUOyzl0bAwh4gdLEaQyDYPvLrZql3pw1ifgb6fhEvZl9iDDehwHZ+dsMzA0Jfww8Xt7COSZuJ/slxc4Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.10.4/ScrollTrigger.min.js" integrity="sha512-v8B8T8l8JiiJRGomPd2k+bPS98RWBLGChFMJbK1hmHiDHYq0EjdQl20LyWeIs+MGRLTWBycJGEGAjKkEtd7w5Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <style> .text_reveal_on_scroll .elementor-heading-title .line { background: linear-gradient(to right, rgb(255, 255, 255) 50%, #333 54%); background-size: 230% 100%; background-position-x: 100%; color: transparent; background-clip: text; -webkit-background-clip: text; } </style> <script> window.addEventListener('load', function() { /* credits for a part of the code : https://www.youtube.com/watch?v=4xZqN80Tp7k */ gsap.registerPlugin(ScrollTrigger); let revealContainers = document.querySelectorAll('.text_reveal_on_scroll .elementor-heading-title'); revealContainers.forEach(e => { let text = new SplitType(e, { types: "lines" }); ScrollTrigger.defaults({ scroller: document.documentElement }); let options = { backgroundPositionX: "0%", stagger: 0.4, scrollTrigger: { trigger: e, scrub: 2, start: "top 70%", end: "bottom 40%" } }; let anim = gsap.to(e.querySelectorAll(".line"), options); window.addEventListener("resize", function() { anim.kill(); text = new SplitType(e, { types: "lines" }); anim = gsap.to(e.querySelectorAll(".line"), options); }); }); }); </script> <?php } add_action( 'wp_footer', 'setup_text_reveal_on_scroll' );
Inline Icon Box on Mobile ( Should work for image box as well)
@media (max-width:767px){ .elementor-icon-box-wrapper { display: flex; } .elementor-icon-box-wrapper > div { padding-left:18px; text-align:left; } }
Merge toggle or accordion FAQPage schema for when you have more than one such element on the page (all need to have the FAQschema active)
/*As is, works for toggle element */ (function () { let FAQdata = Array.from(document.querySelectorAll('.elementor-toggle-item + [type*="+json"]')); if (FAQdata.length === 0) return; /* if there is no FAQPage schema on the page, stop the script */ let firstFaqData = FAQdata.shift(); /* keep only one FAQPage schema to merge the others to */ let firstFaqDataObject = JSON.parse(firstFaqData.innerHTML); /* Merge all the FAQPage schema together */ FAQdata.forEach(faq => { let faqStructuredData = JSON.parse(faq.innerHTML); faqStructuredData.mainEntity.forEach(faqSingle => { firstFaqDataObject.mainEntity.push(faqSingle) }); faq.remove(); }) firstFaqData.innerHTML = JSON.stringify(firstFaqDataObject); /* add back the FAQPage Schema JSON string to the page HTML */ })();
Making your own animations by adapting Elementor native keyframes from animate.css to your keyframes
Copy the CSS code paste in a new code file (elementor-custom code) and change the 100% value in something shorter like 40px. You only have to overwrite the keyframes. @keyframes fadeInUp { from { opacity: 0; transform: translate3d(0, 40px, 0); } to { opacity: 1; transform: translate3d(0, 0, 0); } } Save the CSS file and set condition to entire site and you are good to go. From now one only the fadeinup animation is changed.
Tool for different things
SVG Relative Clip Path tool Yoksel , to get responsive SVG clip paths https://stackoverflow.com/questions/28311741/responsive-clip-path-with-inline-svg
Adjust Elementor scroll offset for menu anchors, and other anchor links (does not work for table of content, use scroll padding top there)
add_action( 'wp_footer', function() { if ( ! defined( 'ELEMENTOR_VERSION' ) ) { return; } ?> <script> window.addEventListener('elementor/frontend/init', function() { elementorFrontend.hooks.addFilter( 'frontend/handlers/menu_anchor/scroll_top_distance', function( scrollTop ) { return scrollTop - 90; } ); } ); </script> <?php } );
JetEngine listing grid to horizontal scroll
<style> .jet-listing-grid__item.jet-listing-grid__item { flex: 0 0 auto; flex-shrink: 0; margin: 10px; width: 47%; } .jet-listing-grid__items.jet-listing-grid__items { display: flex; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; -ms-overflow-style: -ms-autohiding-scrollbar; cursor: grab; /* Change to unset to have the normal cursor */ } .jet-listing-grid__items::-webkit-scrollbar { height: 14px; /* Change to 6px to make the scrollbar smaller, or to 0 to remove it */ } /* Color of scrollbar track */ .jet-listing-grid__items::-webkit-scrollbar-track { background: rgb(255 255 255 / 30%); } /* Color of scrollbar thumb */ .jet-listing-grid__items::-webkit-scrollbar-thumb { background: rgb(255 255 255 / 50%); } .jet-listing-grid__items.active { cursor: grabbing; /* Change to unset to have the normal cursor when mouse button is clicked down */ cursor: -webkit-grabbing; /* idem */ } /* Firefox scroll bar styling */ .jet-listing-grid__items { scrollbar-color: rgb(255 255 255 / 50%) rgb(255 255 255 / 30%); scroll-bar-width: thin; } /* Prevents clicking on links while navigating left or right */ .noclick { pointer-events: none; } </style> <script> document.addEventListener("DOMContentLoaded", function (event) { const horizons = document.querySelectorAll('.jet-listing-grid__items'); let isDown = false; let startX; let scrollLeft; const leftArrow = document.querySelectorAll('.leftarrow'); const rightArrow = document.querySelectorAll('.rightarrow'); horizons.forEach((horizon, i) => { let scrollAmount; scrollAmount = horizon.offsetWidth; /* Change this to 300 to scroll by 300 pixels on each arrow click, for example. As set, it scrolls the width of the elements shown */ window.addEventListener('resize', function () { scrollAmount = horizon.offsetWidth; /* idem , needs to be same value as above */ }); let smoothFunction = function () { horizon.setAttribute('style', 'scroll-behavior:smooth;') }; let clearBehavior = function () { setTimeout(function () { horizon.removeAttribute('style'); }, 771) }; if (leftArrow[i]) { leftArrow[i].setAttribute('tabindex', '0'); rightArrow[i].setAttribute('tabindex', '0'); leftArrow[i].onclick = function () { smoothFunction(); horizon.scrollLeft -= scrollAmount; clearBehavior(); }; rightArrow[i].onclick = function () { smoothFunction(); horizon.scrollLeft += scrollAmount; clearBehavior(); }; } const links = horizon.querySelectorAll('.jet-listing-grid__item'); horizon.addEventListener('mousedown', (e) => { isDown = true; horizon.classList.add('active'); startX = e.pageX - horizon.offsetLeft; scrollLeft = horizon.scrollLeft; }); horizon.addEventListener('mouseleave', () => { isDown = false; horizon.classList.remove('active'); for (var i = 0; i < links.length; i++) { links[i].classList.remove('noclick'); } }); horizon.addEventListener('mouseup', () => { isDown = false; horizon.classList.remove('active'); for (var i = 0; i < links.length; i++) { links[i].classList.remove('noclick'); } }); horizon.addEventListener('mousemove', (e) => { if (!isDown) return; e.preventDefault(); const x = e.pageX - horizon.offsetLeft; const walk = (x - startX) * 1.6; horizon.scrollLeft = scrollLeft - walk; for (var i = 0; i < links.length; i++) { links[i].classList.add('noclick'); } }); }); }); </script>
Multi Currency Currencies Switcher
/* work in progress */ <div class='filters'> <div class='currencies'> <div data-currency='usd' class='currency-usd filter-active'>USD $</div> <div data-currency='eur' class='currency-eur'>EUR €</div> <div data-currency='gbp' class='currency-gbp'>GBP £</div> <div data-currency='aud' class='currency-aud'>AUD $</div> <div data-currency='cad' class='currency-cad'>CAD $</div> </div> <div class='frequencies'> <div data-frequency='monthly' class='monthly'>Monthly</div> <div data-frequency='annual' class='annual filter-active'>Annual</div> </div> </div> <style> .filters{ display: flex; width: 790px; justify-content: space-between; } .filters .currencies, .filters .frequencies{ display: flex; background-color: #dedede; border-radius: 50px; color: #777; text-align: center; cursor: pointer; } .currencies > div, .frequencies > div{ margin: 4px 10px; border-radius: 50px; } .currencies > div{ width:78px; } .frequencies > div{ width: 120px; } .currencies > div.filter-active, .frequencies > div.filter-active{ color:#000; background-color: #fff; font-weight: 600; } </style> <script> document.addEventListener('DOMContentLoaded', function() { let starterPrice = document.querySelector('.starter-price'); let standardPrice = document.querySelector('.standard-price') let plusPrice = document.querySelector('.plus-price'); let currencyFitlers = document.querySelectorAll('.currencies > div'); let frequencies = document.querySelectorAll('.frequencies > div'); let pricesArray = [{starter:'$66',standard:'$77',plus:'$88'},{starter:'€69',standard:'€79',plus:'€89'},{starter:'£63',standard:'£73',plus:'£83'},{starter:'$61',standard:'$71',plus:'$81'},{starter:'$68',standard:'$78',plus:'$88'}]; let pricesObj = { monthly:{ usd:{starter:'$66',standard:'$77',plus:'$88'}, eur:{starter:'€69',standard:'€79',plus:'€89'}, gbp:{starter:'£63',standard:'£73',plus:'£83'}, aud:{starter:'$61',standard:'$71',plus:'$81'}, cad:{starter:'$68',standard:'$78',plus:'$88'}, }, annual:{ usd:{starter:'$63',standard:'$73',plus:'$83'}, eur:{starter:'€67',standard:'€77',plus:'€87'}, gbp:{starter:'£61',standard:'£71',plus:'£81'}, aud:{starter:'$60',standard:'$70',plus:'$80'}, cad:{starter:'$64',standard:'$74',plus:'$84'}, }, }; currencyFitlers.forEach((filter,i,arr) => { filter.addEventListener('click',function(){ arr.forEach(e => e.classList.remove('filter-active')); filter.classList.add('filter-active'); starterPrice.innerHTML = pricesArray[i].starter; standardPrice.innerHTML = pricesArray[i].standard; plusPrice.innerHTML = pricesArray[i].plus; }) }) }); </script>
CSS Check for variables support
Testing for the support of custom properties @supports (--variable: 18px) { body { color: var(--varName); } }
CSS Check for animation support
@supports (animation-name: test) { }
Elementor mobile nav overflowing without scroll fix, so that it fits the viewport, and enables scrolling when needed
.elementor-nav-menu--toggle .elementor-menu-toggle.elementor-active+.elementor-nav-menu--dropdown { max-height: calc(100vh - 100px); overflow: auto; } Adjust the 100px to the approximate height of your header.
Automatically debug CSS sticky problems by finding parents element with overflow set
/* Debug CSS sticky problems */ let stickyShows = document.querySelectorAll('.sp-sticky-yes'); stickyShows.forEach((e, i, arr) => { var parentsElems = []; let a = e; while (a) { parentsElems.unshift(a); a = a.parentNode; } parentsElems = parentsElems.slice(1); parentsElems.forEach((elem) => { if (window.getComputedStyle(elem).overflow === 'hidden' || window.getComputedStyle(elem).overflow === 'hidden auto' || window.getComputedStyle(elem).overflow === 'auto') { elem.style.overflow = 'visible'; if (window.getComputedStyle(elem).overflow === 'visible') { console.log('The following element is set to overflow:hidden. The CSS: overflow-y:auto; max-height:100vh; is required to make compatible with position:sticky CSS.') console.log(elem); } elem.style.removeProperty('overflow'); } if (window.getComputedStyle(elem).overflow === 'hidden' || window.getComputedStyle(elem).overflow === 'hidden auto' || window.getComputedStyle(elem).overflow === 'auto') { console.log("Position:Sticky won't work, this element has overflow:hidden !important set as css somewhere :"); console.log(elem); console.log("Please fix by removing overflow:hidden!important from the element"); } }); });
Image Comparison Change to Video Comparison for image 2
<script> /* automatic comparaison close on mouseleave */ window.addEventListener('load', function () { window.setTimeout(function(){ let comparisonContainers = document.querySelectorAll('.twentytwenty-container'); comparisonContainers.forEach((comparisonContainer, i, arr) => { let imageComparison = comparisonContainer.querySelector('.twentytwenty-before'); let editingIcon = comparisonContainer.querySelector('.twentytwenty-container > div'); let interval; let offsetWidth = comparisonContainer.offsetWidth; window.addEventListener('resize',function(){ offsetWidth = comparisonContainer.offsetWidth; }); comparisonContainer.addEventListener('mouseleave', function () { let rightOffset = +imageComparison.style.clip.split(",")[1].trim().slice(0, -2); interval = setInterval(function () { imageComparison.style.clip = `rect(0px, ${rightOffset++}px, 2468px, 0px)`; editingIcon.style.left = `${rightOffset++}px`; if (rightOffset > offsetWidth) { clearInterval(interval); } }, 10); }); comparisonContainer.addEventListener('mouseenter', function () { clearInterval(interval); }); }); },900); }); /* end of automatic comparaison close on mouseleave */ /* comparison video replacement from data-video attribute */ window.addEventListener('load', function () { window.setTimeout(function(){ let comparisonElements = document.querySelectorAll('.ha-image-compare'); comparisonElements.forEach((comparisonElement, i, arr) => { let comparisonContainer = comparisonElement.querySelector('.twentytwenty-container') let imageToBeReplaced = comparisonElement.querySelector('img:nth-child(2)'); let videoElem = createVideoElem(); comparisonContainer.replaceChild(videoElem, imageToBeReplaced); videoElem.muted = true; videoElem.play(); document.addEventListener('click',function(){ videoElem.play(); }); function createVideoElem(){ let video = document.createElement('video'); let source = document.createElement('source'); video.setAttribute('autoplay',''); video.setAttribute('loop',''); video.setAttribute('muted',''); video.setAttribute('playsinline',''); source.src = comparisonElement.getAttribute('data-video'); source.setAttribute('type','video/mp4'); video.appendChild(source); video.classList.add('videoComparison'); return video; }; }); },900); }); /* end of comparison video replacement */ </script> <style> .videoComparison.videoComparison { height: 100%; object-fit: cover; } </style>
JavaScript check for animation support
var supportsCSS = !!((window.CSS && window.CSS.supports) || window.supportsCSS || false); if (supportsCSS && CSS.supports("animation-name", "test")) { } }
Close Elementor Popup on click of element with class "close_e_popup"
add_action( 'wp_footer', function () { ?> <script> document.addEventListener('click',function(event){ console.log(event.target); console.log(event.target.classList.contains('close_e_popup')); console.log(event.target.closest('.close_e_popup')); if (event.target.classList.contains('close_e_popup') || event.target.closest('.close_e_popup') ){ elementorProFrontend.modules.popup.closePopup( {}, event); } }); </script> <style> .close_e_popup{ cursor:pointer; } </style> <?php } );
Simple Lightbox For Post Content
"add_action( 'wp_footer', function () { ?> <script> let imagesToLightbox = document.querySelectorAll('.elementor-widget-theme-post-content img'); imagesToLightbox.forEach((image,i) => { let name = ""image-link-"" + i; let clone = image.cloneNode(true); let newLink = document.createElement('a'); newLink.href = ""#"" + name; newLink.classList.add('lightbox'); newLink.appendChild(clone); image.parentNode.appendChild(newLink); let newDiv = document.createElement('div'); newDiv.classList.add('lightbox-target'); newDiv.id = name; let closeButton = document.createElement('a'); closeButton.classList.add('lightbox-close'); closeButton.href = ""#no-target""; newDiv.appendChild(closeButton); image.parentNode.appendChild(newDiv); newDiv.appendChild(image); }); /* close lightbox on escape key click, might not be ideal technique */ document.addEventListener('keydown', function(event) { if (event.key == ""Escape"") { document.querySelector('.lightbox-close').click(); } }); </script> <style> /* credits: got the CSS from a codepen */ /* Styles the lightbox, removes it from sight and adds the fade-in transition */ .lightbox-target { position: fixed; top: -100%; width: 100%; background: rgba(0,0,0,.7); height: 100%; opacity: 0; left:0; transition: opacity .5s ease-in-out; overflow: hidden; pointer-events: none; z-index: 99999; } /* Styles the lightbox image, centers it vertically and horizontally, adds the zoom-in transition and makes it responsive using a combination of margin and absolute positioning */ .lightbox-target img { margin: auto; position: absolute; box-sizing: border-box; transition: .5s ease-in-out; top: 0; left:0; right:0; bottom: 0; max-height: 0%; max-width: 0%; box-shadow: 0px 0px 8px rgba(0,0,0,.3); } /* Styles the close link, adds the slide down transition */ a.lightbox-close { display: block; width:50px; height:50px; background: white; color: black; text-decoration: none; position: absolute; top: -80px; right: 0; transition: .5s ease-in-out; pointer-events: all; } /* Provides part of the ""X"" to eliminate an image from the close link */ a.lightbox-close:before { content: """"; display: block; height: 30px; width: 1px; background: black; position: absolute; left: 26px; top:10px; transform:rotate(45deg); } /* Provides part of the ""X"" to eliminate an image from the close link */ a.lightbox-close:after { content: """"; display: block; height: 30px; width: 1px; background: black; position: absolute; left: 26px; top:10px; transform:rotate(-45deg); } /* Uses the :target pseudo-class to perform the animations upon clicking the .lightbox-target anchor */ .lightbox-target:target { opacity: 1; top: 0; bottom: 0; } .lightbox-target:target img { max-height: 96%; max-width: 96%; } .lightbox-target:target a.lightbox-close { top: 0px; } </style> <?php } );"
Taxes WooCommerce setup pour boulangeries
/* Taxes dynamique, si il y a 6 ou plus items taxables, les taxes s'en vont */ add_action( 'woocommerce_calculate_totals', 'set_customer_tax_exempt' ); function set_customer_tax_exempt( $cart ) { if ( is_admin() && ! defined('DOING_AJAX') ) return; $min_amount = 6; $count = 0; ?> <pre> <?php foreach ( WC()->cart->get_cart() as $cart_item ) : $product_id = $cart_item['product_id']; $item_datae = wc_get_product( $product_id )->get_meta('_tax_status'); echo( print_r($item_datae == 'taxable')); if ($item_datae === 'taxable'){ $count = $count + $cart_item['quantity']; } echo ($count); endforeach; ?> </pre> <?php $vat_exempt = WC()->customer->is_vat_exempt(); $condition = $count >= $min_amount; if ( $condition && ! $vat_exempt ) { WC()->customer->set_is_vat_exempt( true ); // Set customer tax exempt } elseif ( ! $condition && $vat_exempt ) { WC()->customer->set_is_vat_exempt( false ); // Remove customer tax exempt } } add_action( 'woocommerce_thankyou', 'remove_customer_tax_exempt' ); function remove_customer_tax_exempt( $order_id ) { if ( WC()->customer->is_vat_exempt() ) { WC()->customer->set_is_vat_exempt( false ); } }
Preserve query parameters across navigating the website
document.addEventListener('DOMContentLoaded', function () { function isImage(url) { return /\.(jpg|jpeg|png|webp|avif|gif|svg)$/.test(url); } Array.from(document.querySelectorAll('a:not([href=""])')).forEach(a => { if (location.hostname === a.hostname && a.closest('.excluded-from-url-change') == null && !(a.pathname == window.location.pathname && a.protocol == window.location.protocol && a.host == window.location.host) && !isImage(a.href)) { a.href += window.location.search; } }); })
Ingredients quantity auto calculator
document.addEventListener('DOMContentLoaded', function() { let servingsInput = document.querySelector('.servings'); let initialServingsQty = +servingsInput.value; let ingredientsQuantitiesElems = document.querySelectorAll('.ingredient-quantity .jet-listing-dynamic-field__content'); let initialIngredientQty = []; ingredientsQuantitiesElems.forEach( (ingredientQtyElem, i) => { initialIngredientQty[i] = +ingredientQtyElem.innerHTML; }); servingsInput.addEventListener('change', function () { let ratio = +servingsInput.value / initialServingsQty; ingredientsQuantitiesElems.forEach( (ingredientQtyElem, i) => { ingredientQtyElem.innerHTML = initialIngredientQty[i] * ratio; }); }); });
Get URL Search Parameters in an object easily
let urlParams; (window.onpopstate = function () { var match, pl = /\+/g, search = /([^&=]+)=?([^&]*)/g, decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, query = window.location.search.substring(1).toLowerCase(); urlParams = {}; while (match = search.exec(query)) urlParams[decode(match[1])] = decode(match[2]); })(); /* example usage urlParams.query */ document.querySelector('.calendly-inline-widget').setAttribute("data-url",`https://calendly.com/forms2mobile/forms2mobile-onboarding/?email=${urlParams.email}&name=${urlParams.firstname}`);
Swipers from 4 class names, requires a custom Swiper.JS file with Swiper global changed to Swipere
add_action( 'wp_footer',function(){ /* Exclude the 4 Swiper test pages, so that if modifications are required, it's easy to edit the code on these pages */ if ( !is_page(889) && !is_page(859) && !is_page(361) && !is_page(627) ){ /* Function that transforms into Swipers the Post element (ideally used with a custom loop), and the Basic Gallery element. For the post element, give it either the class 'swiperposts', for the 3 posts, horizontal swiper, or the class 'swiperverticalposts', for the vertical , single post swiper. Important: set to view only 1 post per column, for desktop, tablet and mobile, for both of these swipers. For the gallery element, give it either the class 'swiperimages', for the 3 images, horizontal swiper, or the class 'swipervertical', for the vertical , single image swiper. Important: set to view only 1 image per column, for desktop, tablet and mobile, for both of these swipers. */ ?> <style> .swiperimages .swiper-containere{ width: 100%; overflow: hidden; } .swiperimages .swiper-wrapper{ height: 300px; width: 100%; transition-timing-function: cubic-bezier(0.78, 0.05, 0.21, 0.94); } .swiperimages .gallery-item img{ height: 340px; width: auto; object-fit: cover; } .swiperimages .swiper-button-forward, .swiperimages .swiper-button-backward{ position: absolute; left: 10px; bottom: -60px; z-index: 3; color: #000; font-size: 34px; padding: 4px; cursor: pointer; font-family: Helvetica Neue, Helvetica; display: flex; height: 54px; } .swiperimages .swiper-button-forward svg, .swiperimages .swiper-button-backward svg{ width:44px; padding:10px; fill:#000; } .swiperimages .swiper-button-forward{ left: unset; right: 10px; } .swiperimages .progress { position: absolute; height:2px; width:calc(100% - 140px); left: 70px; bottom: -34px; z-index: 3; } .swiperimages .progress::before, .swiperimages .progress::after { content:''; background:#000; left: 0; top: 0; width: 100%; position: absolute; height: 2px; } .swiperimages .progress::before{ opacity: 0.4; } .swiperimages .progress::after{ opacity: 1; transform: scaleX(0); } .swiperimages .progress.show-progress::after{ transform: scaleX(1); transition: transform 6s linear; } .swiperposts .swiper-containere{ width: 100%; overflow: hidden; padding-bottom: 40px; } .swiperposts .swiper-wrapper{ height: 300px; width: 100%; transition-timing-function: cubic-bezier(0.78, 0.05, 0.21, 0.94); } .swiperposts .swiper-button-forward, .swiperposts .swiper-button-backward{ position: absolute; left: 10px; bottom: -60px; z-index: 3; color: #000; font-size: 34px; padding: 4px; cursor: pointer; font-family: Helvetica Neue, Helvetica; display: flex; height: 54px; } .swiperposts .swiper-button-forward svg, .swiperposts .swiper-button-backward svg{ width:44px; padding:10px; fill:#000; } .swiperposts .swiper-button-forward{ left: unset; right: 10px; } .swiperposts .progress { position: absolute; height:2px; width:calc(100% - 140px); left: 70px; bottom: -34px; z-index: 3; } .swiperposts .progress::before, .swiperposts .progress::after { content:''; background:#000; left: 0; top: 0; width: 100%; position: absolute; height: 2px; } .swiperposts .progress::before{ opacity: 0.4; } .swiperposts .progress::after{ opacity: 1; transform: scaleX(0); } .swiperposts .progress.show-progress::after{ transform: scaleX(1); transition: transform 6s linear; } .swipervertical .swiper { width: 100%; color: #fff; } .swipervertical .swiper-container{ width: 100%; overflow: hidden; } .swipervertical .swiper-wrapper{ overflow: hidden; height: 470px; } .swipervertical .swiper-slide { transition-timing-function: cubic-bezier(0.78, 0.05, 0.21, 0.94); } .swipervertical .swiper-slide img { display: block; width: 100%; height: 510px; object-fit: cover; transform: translateY(-40px); transition: transform 6.9s linear; } /* Adjust height of single vertical image slider by adding this in Advanced > custom CSS selector.swipervertical .swiper-wrapper{ height: 470px; } selector.swipervertical .swiper-slide img { height: 510px; (needs to be 40px + the value entered just above) } */ .swipervertical .swiper-slide.swiper-slide-active img, .swipervertical .swiper-slide.swiper-slide-duplicate-active img { transform: translateY(0); } .swipervertical .swiper-button-forward, .swipervertical .swiper-button-backward{ position: absolute; right: 50px; top:50%; transform:translateY(-100%); z-index: 3; color: #fff; font-size: 34px; padding: 4px; cursor: pointer; font-family: Helvetica Neue, Helvetica; } .swipervertical .swiper-button-forward{ top:50%; transform:translateY(10%); } .swipervertical .swiper-button-forward svg, .swipervertical .swiper-button-backward svg{ width:44px; padding:13px; fill:#fff; } .swipervertical .progress{ width:3px; height: 60%; position: absolute; right: 30px; top:20%; z-index: 4; } .swipervertical .progress::before, .swipervertical .progress::after{ background-color: #fff; content:''; left: 0; top: 0; position: absolute; height: 100%; width:3px; } .swipervertical .progress::before{ opacity: 0.4; } .swipervertical .progress::after{ opacity: 1; transform-origin: 0 0; transform: scaleY(0); } .swipervertical .progress.show-progress::after{ transition: transform 6s linear; transform: scaleY(1); } /* Swiper vertical posts CSS Below */ /* To change the height of a specific swiper vertical post, add this under Advanced > Custom CSS for the post element selector.swiperverticalposts .swiper-wrapper{ height: 510px; } selector.swiperverticalposts .swiper-slide { height: 510px; } selector.swiperverticalposts .swiper-slide .elementor-background-overlay { height: calc(510px + 40px); } */ .swiperverticalposts .swiper-containere{ width: 100%; overflow: hidden; } .swiperverticalposts .swiper-wrapper{ height: 90vh; width: 100%; overflow: hidden; } .swiperverticalposts .swiper-slide { width: 100%; transition-timing-function: cubic-bezier(0.78, 0.05, 0.21, 0.94); transition-property: transform; height: 90vh; } .swiperverticalposts .swiper-slide .elementor-background-overlay { height: calc(90vh + 40px); transform: translateY(-40px); transition: transform 6.9s linear; } .swiperverticalposts .swiper-slide.swiper-slide-active .elementor-background-overlay, .swiperverticalposts .swiper-slide.swiper-slide-duplicate-active .elementor-background-overlay { transform: translateY(0); } .swiperverticalposts .swiper-button-forward, .swiperverticalposts .swiper-button-backward{ position: absolute; right: 50px; top:50%; transform:translateY(-100%); z-index: 3; color: #fff; font-size: 34px; padding: 4px; cursor: pointer; font-family: Helvetica Neue, Helvetica; } .swiperverticalposts .swiper-button-forward{ top:50%; transform:translateY(10%); } .swiperverticalposts .swiper-button-forward svg, .swiperverticalposts .swiper-button-backward svg{ width:44px; padding:13px; fill:#fff; } .swiperverticalposts .progress{ width:3px; height: 60%; position: absolute; right: 30px; top:20%; z-index: 4; } .swiperverticalposts .progress::before, .swiperverticalposts .progress::after{ background-color: #fff; content:''; left: 0; top: 0; position: absolute; height: 100%; width:3px; } .swiperverticalposts .progress::before{ opacity: 0.4; } .swiperverticalposts .progress::after{ opacity: 1; transform-origin: 0 0; transform: scaleY(0); } .swiperverticalposts .progress.show-progress::after{ transition: transform 6s linear; transform: scaleY(1); } span.swiper-notification { display: none; } </style> <script src="/wp-content/uploads/swiper-bundle.min.js"></script> <script> (function(){ let parents = document.querySelectorAll('.swiperimages .gallery'); parents.forEach((parent, i, arr) => { let children = parent.querySelectorAll('.gallery-item'); let wrapper = document.createElement('div'); wrapper.className = 'swiper-wrapper'; children.forEach((childe) => { wrapper.appendChild(childe); childe.classList.add('swiper-slide'); }); let container = document.createElement('div'); container.className = 'swiper-containere'; let leftArrow = document.createElement('div'); leftArrow.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 69.28 55.65"><path d="M26.82,54.64a3.44,3.44,0,0,0,4.87-4.87L13.2,31.28H65.83a3.45,3.45,0,0,0,3.45-3.45,3.45,3.45,0,0,0-3.45-3.45H13.2L31.69,5.89A3.45,3.45,0,1,0,26.82,1L0,27.83Z"/></svg>`; leftArrow.className = 'swiper-button-backward'; container.append(leftArrow); let rightArrow = document.createElement('div'); rightArrow.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 69.28 55.65"><path d="M42.46,1a3.45,3.45,0,0,0-4.87,4.88L56.08,24.38H3.45a3.45,3.45,0,1,0,0,6.9H56.08L37.59,49.77a3.44,3.44,0,0,0,4.87,4.87L69.28,27.83Z"/></svg>`; rightArrow.className = 'swiper-button-forward'; container.append(rightArrow); let progress4 = document.createElement('div'); progress4.className = 'progress'; container.append(progress4); container.prepend(wrapper); parent.prepend(container); var swiper = new Swipere(container, { slidesPerView: 1, spaceBetween: 20, speed: 930, loop: true, navigation: { nextEl: rightArrow, prevEl: leftArrow, }, breakpoints: { 768: { slidesPerView: 2, }, 1024: { slidesPerView: 3, }, }, autoplay: { delay: 6000, disableOnInteraction: false, }, on: { init: function() { progress4.classList.add('show-progress'); }, beforeTransitionStart: function() { progress4.classList.remove('show-progress'); }, slideChangeTransitionEnd: function() { progress4.classList.add('show-progress'); }, }, }); }); })(); (function(){ let parents = document.querySelectorAll('.swiperposts .ecs-posts'); parents.forEach((parent, i, arr) => { let children = parent.querySelectorAll('article'); let wrapper = document.createElement('div'); wrapper.className = 'swiper-wrapper'; children.forEach((childe) => { wrapper.appendChild(childe); childe.classList.add('swiper-slide'); }); let container = document.createElement('div'); container.className = 'swiper-containere'; let leftArrow = document.createElement('div'); leftArrow.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 69.28 55.65"><path d="M26.82,54.64a3.44,3.44,0,0,0,4.87-4.87L13.2,31.28H65.83a3.45,3.45,0,0,0,3.45-3.45,3.45,3.45,0,0,0-3.45-3.45H13.2L31.69,5.89A3.45,3.45,0,1,0,26.82,1L0,27.83Z"/></svg>`; leftArrow.className = 'swiper-button-backward'; container.append(leftArrow); let rightArrow = document.createElement('div'); rightArrow.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 69.28 55.65"><path d="M42.46,1a3.45,3.45,0,0,0-4.87,4.88L56.08,24.38H3.45a3.45,3.45,0,1,0,0,6.9H56.08L37.59,49.77a3.44,3.44,0,0,0,4.87,4.87L69.28,27.83Z"/></svg>`; rightArrow.className = 'swiper-button-forward'; container.append(rightArrow); let progress4 = document.createElement('div'); progress4.className = 'progress'; container.append(progress4); container.prepend(wrapper); parent.prepend(container); var swiper = new Swipere(container, { slidesPerView: 1, spaceBetween: 20, speed: 930, loop: true, navigation: { nextEl: rightArrow, prevEl: leftArrow, }, breakpoints: { 768: { slidesPerView: 2, }, 1024: { slidesPerView: 3, }, }, autoplay: { delay: 6000, disableOnInteraction: false, }, on: { init: function() { progress4.classList.add('show-progress'); }, beforeTransitionStart: function() { progress4.classList.remove('show-progress'); }, slideChangeTransitionEnd: function() { progress4.classList.add('show-progress'); }, }, }); }); })(); (function(){ document.addEventListener('DOMContentLoaded', function() { if (jQuery('#gallery-1')) { jQuery('#gallery-1').attr('id', ''); } }); let parents = document.querySelectorAll('.swipervertical .gallery'); parents.forEach((parent, i, arr) => { let children = parent.querySelectorAll('.gallery-item'); let wrapper = document.createElement('div'); wrapper.className = 'swiper-wrapper'; children.forEach((childe) => { wrapper.appendChild(childe); childe.classList.add('swiper-slide'); }); let container = document.createElement('div'); container.className = 'swiper-container'; let upArrow = document.createElement('div'); upArrow.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.65 69.28"><path d="M1,26.82a3.45,3.45,0,1,0,4.88,4.87L24.38,13.2V65.83a3.45,3.45,0,1,0,6.9,0V13.2L49.77,31.69a3.45,3.45,0,1,0,4.88-4.87L27.83,0Z"/></svg>`; upArrow.className = 'swiper-button-backward'; container.append(upArrow); let downArrow = document.createElement('div'); downArrow.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.65 69.28"><path d="M54.64,42.46a3.44,3.44,0,0,0-4.87-4.87L31.28,56.08V3.45a3.45,3.45,0,1,0-6.9,0V56.08L5.89,37.59A3.45,3.45,0,0,0,1,42.46L27.83,69.28Z"/></svg>`; downArrow.className = 'swiper-button-forward'; container.append(downArrow); let progress4 = document.createElement('div'); progress4.className = 'progress'; container.append(progress4); container.prepend(wrapper); parent.prepend(container); var swiper4 = new Swipere(container, { effect: "creative", speed: 930, loop: true, navigation: { nextEl: downArrow, prevEl: upArrow, }, autoplay: { delay: 6000, disableOnInteraction: false, }, creativeEffect: { perspective: false, prev: { translate: [0, "20%", -1], }, next: { translate: [0, "-100%", 0], }, }, on: { init: function() { progress4.classList.add('show-progress'); }, beforeTransitionStart: function() { progress4.classList.remove('show-progress'); }, slideChangeTransitionEnd: function() { progress4.classList.add('show-progress'); }, }, }); }); })(); (function(){ let parents = document.querySelectorAll('.swiperverticalposts .ecs-posts'); parents.forEach((parent, i, arr) => { let children = parent.querySelectorAll('article'); let wrapper = document.createElement('div'); wrapper.className = 'swiper-wrapper'; children.forEach((childe) => { wrapper.appendChild(childe); childe.classList.add('swiper-slide'); }); let container = document.createElement('div'); container.className = 'swiper-containere'; let upArrow = document.createElement('div'); upArrow.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.65 69.28"><path d="M1,26.82a3.45,3.45,0,1,0,4.88,4.87L24.38,13.2V65.83a3.45,3.45,0,1,0,6.9,0V13.2L49.77,31.69a3.45,3.45,0,1,0,4.88-4.87L27.83,0Z"/></svg>`; upArrow.className = 'swiper-button-backward'; container.append(upArrow); let downArrow = document.createElement('div'); downArrow.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 55.65 69.28"><path d="M54.64,42.46a3.44,3.44,0,0,0-4.87-4.87L31.28,56.08V3.45a3.45,3.45,0,1,0-6.9,0V56.08L5.89,37.59A3.45,3.45,0,0,0,1,42.46L27.83,69.28Z"/></svg>`; downArrow.className = 'swiper-button-forward'; container.append(downArrow); let progress4 = document.createElement('div'); progress4.className = 'progress'; container.append(progress4); container.prepend(wrapper); parent.prepend(container); var swiper = new Swipere(container, { effect: "creative", speed: 930, loop: true, navigation: { nextEl: downArrow, prevEl: upArrow, }, autoplay: { delay: 6000, disableOnInteraction: false, }, creativeEffect: { perspective: false, prev: { translate: [0, "100%", -1], }, next: { translate: [0, "-100%", 0], }, }, on: { init: function() { progress4.classList.add('show-progress'); }, beforeTransitionStart: function() { progress4.classList.remove('show-progress'); }, slideChangeTransitionEnd: function() { progress4.classList.add('show-progress'); }, }, }); }); })(); </script> <?php } } );
Bot & lighthouse detection
let browserIsBot = /bot|google|Chrome-Lighthouse|baidu|bing|msn|teoma|slurp|yandex/i.test(navigator.userAgent);
Reusing Swiper Parameters by creating a new Swiper instance and passing the previous params
<script> document.addEventListener('DOMContentLoaded', function() { document.addEventListener('click', function() { setTimeout(function(){ if (document.querySelector('.elementor-lightbox .swiper-container') && document.querySelector('.elementor-lightbox .swiper-container').swiper.passedParams.effect != 'fade'){ let swiperCont = document.querySelector('.elementor-lightbox .swiper-container'); let swiperInstance = swiperCont.swiper; let swiperParams = swiperCont.swiper.passedParams; swiperInstance.destroy(); new Swiper(swiperCont, { effect:'fade', fadeEffect: { crossFade: true }, speed: 900, ...swiperParams}) } }, 200); }); }); </script>
CSS ellipsis with line clamp limit lines amount
display: -webkit-box; max-width: 400px; height: 109.2px; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; text-overflow: ellipsis; line-height: 1.625;
scroll event throttling resize event
let scrolling = false; window.addEventListener('scroll', function(){ scrolling = true; }); setInterval(() => { if (scrolling) { scrolling = false; window.dispatchEvent(new Event('resize')); } },300);
Use Javascript Sets to extract an array of unique values from an array with multiple values
function uniqueValuesArray(arr) { tempArr = []; for (let i = 0; i < arr.length; i++) { tempArr.push(arr[i]); } return arr = [...new Set(tempArr)]; } /* how to use : let uniqueArray = uniqueValuesArray(arrayToExtractUniqueFrom); */
Elementor Mobile Navigation bottom nav bar using default nav icon element. make it open up instead of down
nav.elementor-nav-menu--dropdown.elementor-nav-menu__container { bottom: 44px; /* Adjust to the height of your bottom sticky nav bar */ top: inherit!important; transform-origin: bottom; }
Page transitions made easy
function page_transition() { ?> <div class="preloader_outro"></div> <div class="preloader_intro"></div> <style> .preloader_outro { position: fixed; top: -10vh; left: -10vw; right:-10vw; bottom:-10vh; z-index: 9998; background: #E77521; transform:rotate(-10deg) translateX(116vw); transition: transform 0.7s cubic-bezier(0.84, 0, 0.07, 1); } .preloader_outro.loading { transform:none; } .preloader_intro { position: fixed; top: -10vh; left: -10vw; right:-10vw; bottom:-10vh; z-index: 9998; transition: transform 0.7s cubic-bezier(0.84, 0, 0.07, 1); background: #E77521; } .preloader_intro.loaded{ transform:rotate(-10deg) translateX(-116vw); } @media (max-width:1024px){ .preloader_outro { transform:rotate(-10deg) translateX(123vw); } .preloader_intro.loaded{ transform:rotate(-10deg) translateX(-123vw); } } </style> <script> /* let pageTransition = document.querySelector('.preloader_intro'); if (!(sessionStorage.getItem('ep') == '1')) { pageTransition.style.display = 'none'; } */ document.addEventListener('DOMContentLoaded', function() { function isImage(url) { return /\.(jpg|jpeg|png|webp|avif|gif|svg)$/.test(url); } Array.from(document.querySelectorAll('a:not([href=""])')).forEach(a => { if (location.hostname === a.hostname) { if (!(a.pathname == window.location.pathname && a.protocol == window.location.protocol && a.host == window.location.host) && !(a.getAttribute('target') === "_blank") && !isImage(a.href)) { a.classList.add('local'); } } else { a.classList.add('external'); } }); let preloaderIntro = document.querySelectorAll('.preloader_intro,.preloader_image'); let preloaderOutro = document.querySelectorAll('.preloader_outro,.preloader_image'); let localLinks = document.querySelectorAll('.local'); if (document.body.classList.contains('home')){ setTimeout(function() { preloaderIntro.forEach(e => e.classList.add('loaded')); }, 3000); } else { preloaderIntro.forEach(e => e.classList.add('loaded')); } localLinks.forEach(link => { link.addEventListener('click',function(e) { preloaderOutro.forEach(e => e.classList.add('loading')); if ( (e.button == 0 && e.ctrlKey) || (e.button == 0 && e.metaKey)) { preloaderOutro.forEach(e => e.classList.remove('loading')); } } ) }); }); /* Prevent Safari Bug when using Back Button */ window.onpageshow = function(event) { if (event && event.persisted) { window.location.reload(); } }; </script> <?php } add_action( 'wp_head', 'page_transition' );
Popup Card for the Map Facet WP Grid Builder
add_filter( 'wp_grid_builder_map/marker_content', function( $content, $marker ) { ob_start(); $url = wp_get_attachment_url( get_post_thumbnail_id() ); $excerpt = get_the_excerpt(); $marker_id = get_the_ID(); $street = get_post_meta($marker_id, "_street", true); $postal = get_post_meta($marker_id, "_postal", true); $city = get_post_meta($marker_id, "_city", true); $email = get_post_meta($marker_id, "_email", true); $phone = get_post_meta($marker_id, "_phone", true); $website = get_post_meta($marker_id, "_website", true); $emailIcon = '<svg class="e-font-icon-svg e-fab-telegram-plane" aria-hidden="true"><use xlink:href="#fab-telegram-plane"></use></svg>'; $phoneIcon = '<svg class="e-font-icon-svg e-fas-phone-alt" aria-hidden="true"><use xlink:href="#fas-phone-alt"></use></svg>'; $websiteIcon = '<svg class="e-font-icon-svg e-fas-globe" aria-hidden="true"><use xlink:href="#fas-globe"></use></svg>'; $productCategories = get_the_term_list( $marker_id, 'product-category', '', ', ' ); $online = get_the_term_list( $marker_id, 'online', '', ', ' ); if ( $url ) { echo '<div class="wpgb-map-marker-img">'; echo '<div style="background-image:url(' . esc_url( $url ) . ');"></div>'; echo '</div>'; } echo '<div class="wpgb-map-marker-body">'; the_title( '<h3 class="wpgb-map-marker-title marker-title">', '</h3>' ); echo '<span class="marker-sale-channel">'.get_the_term_list( $marker_id, 'sales-channel', '', ', ' ).'</span>'; echo '<div class="marker-street">'.$street.'</div> <div class="marker-postal-city"><span class="marker-postal">'.$postal.'</span><span class="marker-city">'.$city.'</span></div>'; if (!empty($email)) { echo '<div class="marker-email marker-info-with-icon">'.$emailIcon.'<a href="mailto:'.$email.'">'.$email.'</a></div>'; } if (!empty($phone)) { echo '<div class="marker-phone marker-info-with-icon">'.$phoneIcon.'<a href="tel:'.$phone.'">'.$phone.'</a></div>'; } if (!empty($website)) { echo '<div class="marker-website marker-info-with-icon">'.$websiteIcon.'<a href="'.$website.'">'.$website.'</a></div>'; } if (!empty($productCategories)) { echo '<div class="marker-product-category"><span>Sells: </span>'.$productCategories.'</div>'; } if (!empty($online)) { echo '<div class="marker-online"><span>Store type: </span>'.$online.'</div>'; } echo '</div>'; return ob_get_clean(); }, 10, 2 );
Header class section toggle Elementor nav icon
Here you go... this will add the class 'nav-is-opened' to your '.navsection' header section. You don't need to add the class to different elements, just to the parent element (here, the container section). Then, in the css, you use that selector before reaching your , say, logo: .navsection.nav-is-opened .logoclass { logo css styles here for opened nav} .navsection.nav-is-opened .buttonclass { button css styles here for opened nav} Add this in an html element, in your header : <script> document.addEventListener('DOMContentLoaded', function() { jQuery(function($){ $('.elementor-menu-toggle').click(function(){ if ($(this).hasClass('elementor-active')){ $('.navsection').addClass('nav-is-opened'); } else { $('.navsection').removeClass('nav-is-opened'); } }); }); }); </script>
Escape HTML
function escapeHTML(html) { return document.createElement('div').appendChild(document.createTextNode(html)).parentNode.innerHTML; }
header mouse enter add class to header section to get different styling when a sub nav is opened
<script> document.addEventListener('DOMContentLoaded', function() { jQuery(function($){ $('.has-submenu').mouseenter(function(){ $('.navsection').addClass('nav-is-opened'); }); $('.has-submenu').mouseleave(function(){ $('.navsection').removeClass('nav-is-opened'); }); }); }); </script>
Disable Elementor Smooth Anchor Scrolling Feature
<script> /* Disable Elementor Smooth Anchor Scrolling Feature */ window.addEventListener( 'elementor/frontend/init', function() { if ( typeof elementorFrontend === 'undefined' ) { return; } elementorFrontend.on( 'components:init', function() { elementorFrontend.utils.anchors.setSettings('selectors',{}); }); }); </script>
Preloader play until the animation from the GIF has played out, or until the page dom content is loaded, whichever the latest
<div class="vanilla-preloader"></div> <script> let playedOut = false; let domContentLoaded = false; window.setTimeout(function() { playedOut = true }, 5700); document.addEventListener('DOMContentLoaded', function() { domContentLoaded = true; }); let loader = document.querySelector('.vanilla-preloader'); loader.addEventListener('transitionend', () => loader.remove()); let tester = 0; let myTimeout = setInterval(function() { /* console.log('running'); console.log(loader); console.log(playedOut); console.log(domContentL