shortcodes
[iframee url="https://templates.element.how/elementor-reveal-text-on-scroll-template/" theater_by_default="yes" template_post_id="50045"] from element.how [getTemplate data-post-id="36217" data-container-id="target-container-id-here" data-template-name="name-for-downloadable-template-here" data-is-free="true"] // [iframee url="https://templates.element.how/" back_to_default="false" mobile_show="false" theater="false" reload="false" newwindow="false"] // [iframee url="https://templates.element.how/" back_to_default="yes" mobile_show="yes" theater="yes" reload="yes" newwindow="yes" theater_by_default="yes" mobile_by_default="no" template_post_id="123" container_id="container-id" template_name="template-name" is_free="false"] [infobox][/infobox] [info_popup][/info_popup] [click_to_expand title="Comparison of Features"] [/click_to_expand] [warnbox][/warnbox]] If you just want the HTML element with the updated code (now at version 1.4), get it here: [getTemplate data-post-id="37259" data-container-id="html_element" data-is-free-"true" data-button-text="HTML Template"]
copyright
/* Code from https://templates.element.how/elementor-fancy-containers-transition-template/ * Copyright 2024 Element.How * Licensed for personal and commercial use for customers who purchased access * No redistribution or resale permitted */
New tutorial ideas
fixed background videos editor quality of life improvements (remove ads, ai buttons, AI banner, etc) https://codeshare.io/Yzd97Y https://www.ruyton.vic.edu.au/ https://www.facebook.com/groups/916625091796611/ https://graphek.de fancy swiper: https://www.youtube.com/watch?v=sT5KRoOtEpo page transition tutorial copy paste text from text editor or heading element disctinction : https://www.choate.edu/
Basic
<script> document.addEventListener('DOMContentLoaded', function() { }); </script>
Basic jQuery
<script> document.addEventListener('DOMContentLoaded', function() { jQuery(function($){ }); }); </script>
Explanation of z index navigation nav icon popup stacking context
On your parent container, set the z index to 9999999 That will show the whole header when the popup is opened. It's the only way you can get this to work with your currnet setup. Trying to just show the icon, but not the rest of the header is possible if you don't set the parent container to Sticky. If you set it to Sticky, Elementor adds a z-index value, creating a new stacking context. Because the nav icon is within that stacking context, it can't have a greater or lesser effective z-index value than that parent sticky container. So while sticky, the only choice is header above or below the popup. If not sticky, and no z index are set anywhere except the icon, then the popup and icon share the same stacking context, so their z-index can interact. If you set the icon to greater than that of the popup, it will be above the popup.
Reliable well working intersection observer
to get a reliable intersection observer: 1. The observed element should not have any parent with overflow:hidden , otherwise the new scrolling context messes with the IO on the root (document) element One way to solve this is to append proxy elements to be observed directly to the <body>, position them excatly where needed, and observed them instead of the element that has (or might have at some point) a overflow:hidden parent 2. The rooMargin should not result in a 0px view, so if bottom is -1000px, top is 0px (aka '0px 0px -1000px 0px' ) then if the window.innerHeight is less than 1000px, the observed root will be 0px tall and this bugs, mostly on other brwosers than Chrome I think
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; }
screen ready only sr-only class from google
.sr-only { border: 0; clip: rect(1px, 1px, 1px, 1px); clip-path: inset(50%); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; white-space: nowrap; }
Stop Vimeos from playing in the Popup from my tutorial to add popup to each elementor pro loop item
/* Stop playing Vimeos when closing or changing the popups */ window.addEventListener('load', function () { /* load the Vimeo api */ let tag = document.createElement('script'); tag.src = "https://player.vimeo.com/api/player.js"; let firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); /* add event listener to the popup */ document.addEventListener('ehow_popup_changed_or_closed', function () { /* get all vimeos and pause() them through the Vimeo api*/ let vimeos = document.querySelectorAll('.popupSection iframe'); vimeos.forEach((vimeo) => { let player = new Vimeo.Player(vimeo); player.pause(); }); }); });
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 */
Splide JS make a thumbnail carousel
window.addEventListener('load', () => { /* get the instances ID from the console, by entering `bricksData.splideInstances` */ const coreCarouselId = 'eqxyrm', thumbnailCarouselId = 'lgkfnt', core = bricksData.splideInstances[coreCarouselId], thumbnail = bricksData.splideInstances[thumbnailCarouselId] if (core && thumbnail) { core.sync(thumbnail) } document.addEventListener('click', (e) => { const slide = e.target.closest(`#brxe-${thumbnailCarouselId} .splide__slide`) if (slide) { const ariaLabel = slide.getAttribute('aria-label'); const index = parseInt(ariaLabel.split(' ')[0]) - 1; thumbnail.go(index); } }) });
Make the Elementor Masonry loop grid columns actually even (avoid a column having 2 or 3 items at the bottom while the other is empty)
selector .elementor-loop-container.elementor-grid.elementor-posts-masonry { display: block; columns: 2; --column-gap: 30px; column-gap: var(--column-gap); } selector .e-loop-item { margin-top: 0px !important; padding-top: var(--column-gap); }
JetSmartFilters Radio filters with sub categories / taxonomies filters, make the sub filters a drop down
document.addEventListener('DOMContentLoaded', function () { let childHeight = 0; let filterTree = document.querySelector('.jet-list-tree'); document.querySelectorAll('.jet-list-tree__parent').forEach(function (parent, i) { let sibling = parent.nextElementSibling; if (sibling && sibling.classList.contains('jet-list-tree__children')) { if (window.innerWidth > 768) { // Calculate the desired left value let parentLeft = parent.offsetLeft; let parentWidth = parent.offsetWidth; let childWidth = sibling.offsetWidth; let leftValue = parentLeft + (parentWidth / 2) - (childWidth / 2); // Set the left value to the sibling sibling.style.left = leftValue + 'px'; } /* in the parent, find the input and give it an ID with i */ let parentInput = parent.querySelector('.jet-radio-list__input'); parentInput.id = 'jet-radio-list__input-parent-' + i; /* get the first child of sibling, get the HTML, and prepend it to sibling */ let firstChild = sibling.children[0]; let firstChildHTML = firstChild.outerHTML; sibling.innerHTML = firstChildHTML + sibling.innerHTML; /* select first child again, find .jet-radio-list__label and change text to Toutes */ firstChild = sibling.children[0]; let label = firstChild.querySelector('.jet-radio-list__label'); label.innerHTML = 'Toutes'; /* select first child again, find .jet-radio-list__input and remove it */ let input = firstChild.querySelector('.jet-radio-list__input'); input.remove(); /* select first child again, find .jet-radio-list__label and give it a FOR attribute matching the ID of the parent radio */ let labelElem = firstChild.querySelector('.jet-radio-list__item'); labelElem.setAttribute('for', 'jet-radio-list__input-parent-' + i); if (sibling.offsetHeight > childHeight) { childHeight = sibling.offsetHeight; } } }); filterTree.style.setProperty('--sub-cat-height', childHeight + 'px'); });
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); } }
Breadcrumbs shortcode
<?php 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>'; /* remove '/products' from esc_url(get_term_link($first_term)) */ $breadcrumb .= '<a href="' . str_replace('/products', '', 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');
Improve Elemntor WordPres Menu elment on desktop and mobile (change arrow, add divider, add diferrent font style for sub nav items on mobile)
/* remove the Elementor default border left CSS on sub menu items */ selector .sub-menu a { border-left: 0; } /* make drop down full width on desktop */ selector .sub-menu::before { content: ''; position: absolute; width: 200vw; height: 100%; background-color: var(--e-global-color-primary); left: -100vw; } /* arrows */ selector .fa-caret-down::before { content: ''; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14.121' height='7.811' viewBox='0 0 14.121 7.811'%3E%3Cpath id='Path_163' data-name='Path 163' d='M18,24l6-6-6-6' transform='translate(25.061 -16.939) rotate(90)' fill='none' stroke='%23f1f0ed' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5'/%3E%3C/svg%3E"); background-size: contain; background-position: center center; background-repeat: no-repeat; display: block; width: 14px; /* adjust size of arrow here */ height: 7px; /* adjust size of arrow here */ } /* up arrow */ selector [aria-expanded="true"] .fa-caret-down::before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14.121' height='7.811' viewBox='0 0 14.121 7.811'%3E%3Cpath id='Path_161' data-name='Path 161' d='M18,24l6-6-6-6' transform='translate(-10.939 24.75) rotate(-90)' fill='none' stroke='%23f1f0ed' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5'/%3E%3C/svg%3E"); } /* mobile */ @media (max-width: 1024px) { selector .elementor-nav-menu { padding-top: 10px; /* padding top on mobile before the first item */ } selector nav.elementor-nav-menu__container>ul>.menu-item>a { font-size: 28px !important; /* font size for first level items on mobile */ font-weight: 400 !important; /* font weight */ justify-content: space-between; padding-right: calc(3% + 5px); /* position arrow here, use the PX */ } selector nav.elementor-nav-menu__container a { font-family: var(--e-global-typography-secondary-font-family), Sans-serif !important; } /* size of mobile arrows */ selector .fa-caret-down::before { width: 21px; height: 17px; } /* line between items */ selector nav.elementor-nav-menu__container>ul>.menu-item:not(:first-child)::before { content: ''; position: absolute; left: 3%; right: 3%; top: -1px; height: 1px; /* height of separator line */ background-color: #ffffff; /* color of line */ } selector .elementor-nav-menu--dropdown .sub-menu a { /* padding between items in the sub menu */ padding-top: 13px; padding-bottom: 13px; } selector .elementor-nav-menu--dropdown .sub-menu li:first-child a { padding-top: 0px; /* padding top of first item in sub menu */ } selector .elementor-nav-menu--dropdown .sub-menu li:last-child a { padding-bottom: 23px; /* padding bottom of last item in sub menu */ } }
Multie line animated line by line heading clip out up, clip in, requries a container, then <h2> headings in it
<!-- Include the SplitType library --> <script src="https://unpkg.com/split-type"></script> <script> (function() { const intervalTime = 6000; // Set the interval time in milliseconds const container = document.querySelector('.animatedHeadingContainer'); const headings = container.querySelectorAll('h2'); let currentIndex = 0; // Split each line using SplitType headings.forEach(heading => { new SplitType(heading, { types: 'lines, words' }); }); function updateActiveHeading() { headings.forEach((heading, index) => { if (index === currentIndex) { heading.classList.add('isActive'); } else { heading.classList.remove('isActive'); } }); currentIndex = (currentIndex + 1) % headings.length; } // Initial call to set the first heading active updateActiveHeading(); // Set interval to update the active heading setInterval(updateActiveHeading, intervalTime); })(); </script> <style> .animatedHeadingContainer { position: relative; } .animatedHeadingContainer h2 { overflow: hidden; } .animatedHeadingContainer h2:not(:first-child) { position: absolute; width: 100%; top: 0; left: 0; } .animatedHeadingContainer .line { display: block; overflow: hidden; } .animatedHeadingContainer h2.isActive .word { animation: slideIn 0.5s both 0.3s; } .animatedHeadingContainer h2.isActive .line:nth-child(2) .word { animation-delay: 0.4s; } .animatedHeadingContainer h2.isActive .line:nth-child(3) .word { animation-delay: 0.5s; } .animatedHeadingContainer h2.isActive .line:nth-child(4) .word{ animation-delay: 0.6s; } .animatedHeadingContainer h2.isActive .line:nth-child(5) .word { animation-delay: 0.7s; } .animatedHeadingContainer h2:not(.isActive) .word { animation: slideOut 0.5s both; } .animatedHeadingContainer h2:not(.isActive) .line:nth-child(2) .word { animation-delay: 0.1s; } .animatedHeadingContainer h2:not(.isActive) .line:nth-child(3) .word { animation-delay: 0.2s; } .animatedHeadingContainer h2:not(.isActive) .line:nth-child(4) .word { animation-delay: 0.3s; } .animatedHeadingContainer h2:not(.isActive) .line:nth-child(5) .word { animation-delay: 0.4s; } @keyframes slideIn { 0% { transform: translateY(100%); clip-path: inset(100% 0 0 0); } 100% { transform: translateY(0); clip-path: inset(0 0 0 0); } } @keyframes slideOut { 0% { transform: translateY(0); clip-path: inset(0 0 0 0); } 100% { transform: translateY(-100%); clip-path: inset(0 0 100% 0); } } </style>
in a header with two rows, show only the bottom row when scrolling down and show both rows when scrolling up
/* in a header with two rows, show only the bottom row when scrolling down and show both rows when scrolling up */ document.addEventListener("DOMContentLoaded", function () { let prevScrollpos = window.scrollY; let headerElement = document.getElementById("hide-header"); let headerContainer = document.getElementById("header-container"); let mobileNav = document.getElementById("mobile-nav"); let headerHeight = headerElement.offsetHeight; /* set css custom property for header height */ mobileNav.style.setProperty("--header-height", headerHeight + "px"); window.addEventListener("scroll", function () { let currentScrollPos = window.scrollY; headerHeight = headerElement.offsetHeight; if (prevScrollpos > currentScrollPos || currentScrollPos <= 200) { headerContainer.style.transform = "none"; } else { headerContainer.style.transform = "translateY(-" + headerHeight + "px)"; } prevScrollpos = currentScrollPos; }); });
Bricks sync two sliders and slide on click of thumbnail slider image
window.addEventListener('load', () => { /* get the instances ID from the console, by entering `bricksData.splideInstances` */ const coreCarouselId = 'eqxyrm', thumbnailCarouselId = 'lgkfnt', core = bricksData.splideInstances[coreCarouselId], thumbnail = bricksData.splideInstances[thumbnailCarouselId] if (core && thumbnail) { core.sync(thumbnail) } document.addEventListener('click', (e) => { const slide = e.target.closest(`#brxe-${thumbnailCarouselId} .splide__slide`) if (slide) { const ariaLabel = slide.getAttribute('aria-label'); const index = parseInt(ariaLabel.split(' ')[0]) - 1; thumbnail.go(index); } }) });
set iOS theme color with PHP or JS
(function() { let themeColor = "#ecd96f"; // Change this to adjust the theme color let metaTag = document.querySelector('meta[name="theme-color"]'); if (metaTag) { metaTag.setAttribute("content", themeColor); } else { metaTag = document.createElement("meta"); metaTag.setAttribute("name", "theme-color"); metaTag.setAttribute("content", themeColor); document.head.appendChild(metaTag); } })(); function ios_set_theme_color() { $theme_color = '#ecd96f'; /* Change this to set the desired theme color */ echo '<meta name="theme-color" content="' . esc_attr($theme_color) . '">'; } add_action('wp_head', 'ios_set_theme_color');
perfectly smooth pulsing circle button, for a circle button with 1:1 aspect ratio
<script> (function() { const circleButton = document.querySelector('.circleButton'); // Find the first .circleButton element let intervalId = null; // To keep track of the interval ID function toggleExpansion() { // Toggle the 'isExpanded' class if (circleButton.classList.contains('isExpanded')) { circleButton.classList.remove('isExpanded'); } else { circleButton.classList.add('isExpanded'); } } function startInterval() { // Set the interval to 1.5 seconds and store the interval ID intervalId = setInterval(toggleExpansion, 750); } function stopInterval() { // Clear the interval using the stored ID clearInterval(intervalId); } // Set up the initial interval startInterval(); // Add event listeners for mouseenter and mouseleave circleButton.addEventListener('mouseenter', function() { stopInterval(); // Stop the interval when the mouse enters circleButton.classList.remove('isExpanded'); // Immediately remove the class }); circleButton.addEventListener('mouseleave', function() { startInterval(); // Restart the interval when the mouse leaves }); })(); </script> .circleButton { position: relative; } .circleButton::after { content: ''; position: absolute; width: 100%; height: 100%; border-radius: 50%; top: 0; left: 0; border: 2px solid #333; transform: scale(0.9); transition: transform 0.75s ease; } .circleButton.isExpanded::after { transform: scale(1.1); } .circleButton.circleButton:hover::after { transform: scale(1.1); /* Scale up on hover */ transition: transform 0.3s ease; } .circleButton.circleButton:active::after { transform: scale(0.85); /* Scale down when pressed */ transition: transform 0.2s ease; }
WooCommerce Attribute radio lists on the cart page to a <select> dropdown
(function () { /* to prevent the fouc */ const uls = document.querySelectorAll('.wcsatt-options'); uls.forEach(ul => { // Hide the UL but keep it accessible ul.style.visibility = 'hidden'; ul.style.position = 'absolute'; ul.style.width = '1px'; ul.style.height = '1px'; ul.style.overflow = 'hidden'; }); }()) window.addEventListener('DOMContentLoaded', function () { function radioToSelect() { // Find all UL elements with the class 'wcsatt-options' const uls = document.querySelectorAll('.wcsatt-options'); // Process each UL element found uls.forEach(ul => { let select; let td = ul.parentElement; // the TD element containing the UL try { // Hide the UL but keep it accessible ul.style.visibility = 'hidden'; ul.style.position = 'absolute'; ul.style.width = '1px'; ul.style.height = '1px'; ul.style.overflow = 'hidden'; // Retrieve all radio inputs inside this UL const radios = ul.querySelectorAll('input[type="radio"]'); if (!radios.length) throw new Error('Radio buttons not found.'); // Create a select element select = document.createElement('select'); select.setAttribute('name', 'subscription-options'); select.setAttribute('id', 'subscription-options'); let selectedIndex = 0; // To track the index of the selected radio // Populate the select element with options derived from the radio inputs radios.forEach((radio, index) => { const label = radio.nextElementSibling; if (!label) throw new Error('Label for a radio button not found.'); const option = document.createElement('option'); option.value = radio.value; option.textContent = label.textContent.trim(); select.appendChild(option); // Check if the radio is the one that's checked if (radio.checked) { selectedIndex = index; } }); // Append the select element to the TD td.appendChild(select); // Set the select element to reflect the checked radio button select.selectedIndex = selectedIndex; // Add event listener to the select element to sync changes with radio inputs select.addEventListener('change', function () { const value = this.value; radios.forEach(radio => { radio.checked = (radio.value === value); }); }); } catch (error) { console.error('Failed to initialize custom select element:', error); // Revert changes: make the UL visible again ul.style.visibility = ''; ul.style.position = ''; ul.style.width = ''; ul.style.height = ''; ul.style.overflow = ''; // Remove the select element if it was added if (select && td.contains(select)) { td.removeChild(select); } } }); } // Run the function once when the page loads radioToSelect(); jQuery(document.body).on('updated_wc_div', function () { radioToSelect(); }); });
Calculator PDF creation
window.addEventListener('load', function () { // Function to remove the loading overlay function removeLoadingOverlay() { const overlay = document.getElementById('loading-overlay'); if (overlay) { document.body.removeChild(overlay); } } // Function to convert the current page to PDF function convertToPDF() { const element = document.body; // The DOM element to capture const options = { margin: 0, filename: 'tintelligence.pdf', image: { type: 'jpeg' }, html2canvas: { scale: 2 }, jsPDF: { unit: 'mm', format: [400, 1340], // Custom format orientation: 'portrait', // Set orientation }, }; // Convert the element to PDF html2pdf().set(options).from(element).save().then(() => { // Remove the loading overlay after the PDF is ready removeLoadingOverlay(); }); } // Function to check if the scroll position is at the top function checkAndScrollToTop(callback, attempts) { const maxAttempts = attempts || 6; // Maximum attempts to scroll to the top const attemptInterval = 600; // Time in milliseconds between checks function tryScroll() { if (window.scrollY > 0 && maxAttempts > 0) { window.scrollTo({ top: 0, behavior: 'smooth' }); setTimeout(tryScroll, attemptInterval); // Retry after a short delay } else { callback(); // If at the top or max attempts reached, call the callback } } tryScroll(); } document.getElementById('html2pdf').addEventListener('click', function () { createLoadingOverlay(); // Show loading overlay // Check if the scroll position is at the top and proceed with conversion checkAndScrollToTop(convertToPDF, 10); // 10 attempts to ensure it's at the top }); });
Update the Calculator with the values from the URL parameters
document.addEventListener("DOMContentLoaded", function () { // Get URL parameter by name function getUrlParam(name) { const regex = new RegExp(`[?&]${name}=([^&]*)`); const match = window.location.search.match(regex); return match ? decodeURIComponent(match[1]) : null; } // Populate form fields from URL parameters function populateFieldsFromURL() { const fields = document.querySelectorAll('.calculator-wrapper input, .calculator-wrapper select, .calculator-wrapper textarea'); fields.forEach((field) => { const value = getUrlParam(field.name); if (value !== null) { if (field.type === 'checkbox') { field.checked = value == 'yes'; } else { field.value = value; } } }); if (window.location.search) { document.querySelector('.font-button').click(); } } // Create a URL based on current field values function createSaveURL() { const fields = document.querySelectorAll('.calculator-wrapper input, .calculator-wrapper select, .calculator-wrapper textarea'); const params = new URLSearchParams(); fields.forEach((field) => { let value; if (field.type === 'checkbox') { value = field.checked ? 'yes' : 'no'; } else { value = field.value; } if (value && value !== '') { params.append(field.name, encodeURIComponent(value)); } }); const baseUrl = window.location.href.split('?')[0]; // get URL without query params const saveUrl = `${baseUrl}?${params.toString()}`.replace('#', ''); console.log(`Save URL: ${saveUrl}`); // You can copy this to the clipboard or use another action return saveUrl; } // Populate fields on page load window.addEventListener('load', populateFieldsFromURL); // JavaScript code to update href on click const html2pdfButton = document.querySelector(".html2pdf"); const savedURLButton = document.querySelector(".savedURLbutton"); const copyURLButton = document.querySelector(".copyFormURL"); // Event listener to generate the save URL and set it on the savedURLButton html2pdfButton.addEventListener("click", function () { const newURL = createSaveURL(); savedURLButton.setAttribute("href", newURL); // set the href attribute }); // Event listener to copy the generated URL to the clipboard and display an alert copyURLButton.addEventListener("click", function () { const urlToCopy = createSaveURL(); // Create the save URL navigator.clipboard.writeText(urlToCopy) // Write the URL to the clipboard .then(function () { alert("Calculation URL copied to clipboard successfully."); // Alert on success }) .catch(function (error) { console.error("Failed to copy the URL:", error); }); }); });
Swiper Fraction hide total count, move progress to upper right corner
/* swiper fraction pagination, hide total count */ .swiper-pagination.swiper-pagination-fraction.swiper-pagination-horizontal { visibility: hidden; } span.swiper-pagination-current { visibility: visible; margin-right: -9px; } span.swiper-pagination-total { display: none; } /* END OF swiper fraction pagination, hide total count */ /* swiper progressbar pagination, position in upper right corner */ .swiper-pagination.swiper-pagination-progressbar.swiper-pagination-horizontal { left: calc(100% - 200px); right: 80px; width: initial; top: -18px; }
Corners Calc to make reverse border radius design
%root%::before { content:''; --corner-size: 100px; position: absolute; width: var(--corner-size); height: var(--corner-size); top: 0; right: 0; z-index: 1; border-radius: 50%; box-shadow: var(--corner-size) calc( var(--corner-size) * -1 ) 0px calc( var(--corner-size) * (5/8) ) #000; }
WooCommerce All Product Subscription Change Radio Input List to a Select, Synchronize them
(function () { /* to prevent the fouc */ const uls = document.querySelectorAll('.wcsatt-options'); uls.forEach(ul => { // Hide the UL but keep it accessible ul.style.visibility = 'hidden'; ul.style.position = 'absolute'; ul.style.width = '1px'; ul.style.height = '1px'; ul.style.overflow = 'hidden'; }); }()) window.addEventListener('DOMContentLoaded', function () { function radioToSelect() { // Find all UL elements with the class 'wcsatt-options' const uls = document.querySelectorAll('.wcsatt-options'); // Process each UL element found uls.forEach(ul => { let select; let td = ul.parentElement; // the TD element containing the UL try { // Hide the UL but keep it accessible ul.style.visibility = 'hidden'; ul.style.position = 'absolute'; ul.style.width = '1px'; ul.style.height = '1px'; ul.style.overflow = 'hidden'; // Retrieve all radio inputs inside this UL const radios = ul.querySelectorAll('input[type="radio"]'); if (!radios.length) throw new Error('Radio buttons not found.'); // Create a select element select = document.createElement('select'); select.setAttribute('name', 'subscription-options'); select.setAttribute('id', 'subscription-options'); let selectedIndex = 0; // To track the index of the selected radio // Populate the select element with options derived from the radio inputs radios.forEach((radio, index) => { const label = radio.nextElementSibling; if (!label) throw new Error('Label for a radio button not found.'); const option = document.createElement('option'); option.value = radio.value; option.textContent = label.textContent.trim(); select.appendChild(option); // Check if the radio is the one that's checked if (radio.checked) { selectedIndex = index; } }); // Append the select element to the TD td.appendChild(select); // Set the select element to reflect the checked radio button select.selectedIndex = selectedIndex; let updateCartTimeout; // Add event listener to the select element to sync changes with radio inputs select.addEventListener('change', function () { const value = this.value; radios.forEach(radio => { radio.checked = (radio.value === value); }); clearTimeout(updateCartTimeout); updateCartTimeout = setTimeout(() => { let cartUpdateButton = document.querySelector('button[name=update_cart]'); if (cartUpdateButton) cartUpdateButton.click(); }, 1400); }); } catch (error) { console.error('Failed to initialize custom select element:', error); // Revert changes: make the UL visible again ul.style.visibility = ''; ul.style.position = ''; ul.style.width = ''; ul.style.height = ''; ul.style.overflow = ''; // Remove the select element if it was added if (select && td.contains(select)) { td.removeChild(select); } } }); } // Run the function once when the page loads radioToSelect(); jQuery(document.body).on('updated_wc_div', function () { radioToSelect(); }); });
WS Form improved Upload Field
/* in case the label is hidden, undo */ [type="file"].wsf-field { position: static; width: initial; height: initial; margin: initial; padding: initial; overflow: initial; clip: initial; } .uploadFormFieldWrapper [type="file"].wsf-field { position: absolute; visibility: hidden; opacity: 0; left: -200vw; } .uploadFormFieldWrapper { position: relative; background-color: #ffffff; border: 1px dashed #c7bdab; border-radius: 80px; padding: 20px; transition: background-color 200ms ease-in-out, border-color 200ms ease-in-out, box-shadow 200ms ease-in-out; transition-behavior: normal, normal, normal; transition-duration: 200ms, 200ms, 200ms; transition-timing-function: ease-in-out, ease-in-out, ease-in-out; transition-delay: 0s, 0s, 0s; transition-property: background-color, border-color, box-shadow; cursor: pointer; } .uploadFormFieldWrapper label { background-color: #eee; border: 1px dashed #c7bdab; border-radius: 100px; padding: 20px 20px 0; transition: background-color 200ms ease-in-out, border-color 200ms ease-in-out, box-shadow 200ms ease-in-out; transition-behavior: normal, normal, normal; transition-duration: 200ms, 200ms, 200ms; transition-timing-function: ease-in-out, ease-in-out, ease-in-out; transition-delay: 0s, 0s, 0s; transition-property: background-color, border-color, box-shadow; cursor: pointer; } .fileUpload_labelContent { display: flex; flex-direction: column; text-align: center; align-items: center; display: flex; align-items: center; justify-content: center; flex-direction: column; cursor: pointer; color: #333; font-family: Roobert; font-size: 1rem; font-weight: inherit; line-height: 1.4; -webkit-tap-highlight-color: transparent; text-size-adjust: 100%; cursor: pointer; outline: none; box-sizing: inherit; color: #adadad; margin-bottom: 20px; text-align: center; } .fileUpload_labelContent.hide { display: none } .fileUpload_uploadHint { color: rgba(0, 0, 0, 0.4) } .fileUpload_labelContent svg { width: 40px; height: 40px; margin-bottom: 5px; opacity: 0.5 } .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%; background-color: #ffffff; border-radius: 100px; } .fileUpload_fileName { margin-bottom: 10px; overflow: hidden; text-overflow: ellipsis; color: #333333; } 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: relative; cursor: pointer; width: 31px; height: 21px; opacity: 0.5; } .fileUpload_removeFile:hover { opacity: 1; } .fileUpload_removeFile:before, .fileUpload_removeFile:after { position: absolute; left: 15px; content: ' '; height: 21px; width: 2px; background-color: #777777; } .fileUpload_removeFile:before { transform: rotate(45deg); } .fileUpload_removeFile:after { transform: rotate(-45deg); } .fileUpload_uploadNowMessage { position: absolute; width: 100%; height: 100%; left: 0; top: 0; background-color: rgba(0, 0, 0, 0.8); display: flex; align-items: center; justify-content: center; flex-direction: column; z-index: 100; transition: opacity 0.3s ease-in-out; color: #ffffff; font-size: 2rem; opacity: 0; pointer-events: none; outline: 6px dashed #ffffff; border-radius: 80px; outline-offset: -20px; } .fileUpload_uploadNowMessage_icon { color: #ffffff; /* color of the icon in upload now message */ } .fileUpload_uploadNowMessage_icon svg { width: 60px; height: auto; } .fileUpload_uploadNowMessage__visible { opacity: 1; } .uploadFormFieldWrapper .wsf-invalid-feedback { padding-left: 20px; } (function () { let uploadMessage = `Téléchargez votre CV ou tout autre fichier pertinent. Max. taille du fichier : 16 Mo. *`; let uploadNowMessage = `Déposez votre fichier ici`; let iconSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" 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></div>`; let uploadFieldElems = document.querySelectorAll('.uploadFormFieldWrapper'); uploadFieldElems.forEach(init); function init(uploadField) { if (uploadField.classList.contains('upload_init')) { return; } uploadField.classList.add('upload_init'); let allSelectedFiles = {}; // Store the file objects here let formLabel = uploadField.querySelector('label'); formLabel.innerHTML = labelFieldContent; let fileUploadInput = uploadField.querySelector('input'); let uploadPreviewField = document.createElement('ul'); uploadPreviewField.classList.add('fileUpload_previewField'); formLabel.insertAdjacentElement('afterEnd', uploadPreviewField); let uploadNowMessageDiv; function appendUploadNowMessage() { uploadNowMessageDiv = document.createElement('div'); uploadNowMessageDiv.classList.add('fileUpload_uploadNowMessage'); uploadNowMessageDiv.innerHTML = `<div class="fileUpload_uploadNowMessage_icon">${iconSVG}</div><div class="fileUpload_uploadNowMessage_text">${uploadNowMessage}</div>`; uploadField.appendChild(uploadNowMessageDiv); } appendUploadNowMessage(); ['dragenter', 'dragover'].forEach(eventName => { uploadField.addEventListener(eventName, () => { uploadNowMessageDiv.classList.add('fileUpload_uploadNowMessage__visible'); setTimeout(() => { uploadNowMessageDiv.classList.remove('fileUpload_uploadNowMessage__visible'); }, 3900); }); }); ['mouseleave', 'dragleave', 'drop'].forEach(eventName => { uploadField.addEventListener(eventName, () => { uploadNowMessageDiv.classList.remove('fileUpload_uploadNowMessage__visible'); }); }); fileUploadInput.addEventListener('change', (event) => { addOrUpdateFileList(Array.from(event.target.files)); // Convert FileList to Array updateList(); }); uploadField.addEventListener("drop", (event) => { event.preventDefault(); if (event.dataTransfer.files.length) { addOrUpdateFileList(Array.from(event.dataTransfer.files)); updateList(); } }); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { uploadField.addEventListener(eventName, preventDefaults, false) }); function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } function addOrUpdateFileList(newFiles) { newFiles.forEach(file => { const fileId = `${file.name}_${file.size}`; allSelectedFiles[fileId] = file; }); refreshFileInput(); } function refreshFileInput() { const dt = new DataTransfer(); Object.values(allSelectedFiles).forEach(file => dt.items.add(file)); fileUploadInput.files = dt.files; console.log(fileUploadInput.files); console.log(dt.files); } function updateList() { uploadPreviewField.innerHTML = ''; Object.values(fileUploadInput.files).forEach((file, index) => { let uploadedFileItem = document.createElement('li'); uploadedFileItem.classList.add('fileUpload_filePreview'); 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.textContent = file.name; fileInfo.appendChild(fileName); uploadedFileItem.appendChild(fileInfo); let removeFileItem = document.createElement('span'); removeFileItem.classList.add('fileUpload_removeFile'); removeFileItem.addEventListener('click', function () { removeFileFromFileList(file.name, file.size); updateList(); }); let removeFileItemText = document.createElement('span'); removeFileItemText.textContent = 'Remove'; removeFileItemText.classList.add('elementor-screen-only'); removeFileItem.appendChild(removeFileItemText); uploadedFileItem.appendChild(removeFileItem); if (file.type.startsWith("image/")) { let previewImage = new Image(); previewImage.classList.add("fileUpload_previewImage"); previewImage.src = URL.createObjectURL(file); uploadedFileItem.prepend(previewImage); } else { let fileIcon = document.createElement('i'); fileIcon.classList.add("fileUpload_previewIcon"); uploadedFileItem.prepend(fileIcon); } uploadPreviewField.appendChild(uploadedFileItem); }); } function removeFileFromFileList(fileName, fileSize) { const fileId = `${fileName}_${fileSize}`; delete allSelectedFiles[fileId]; refreshFileInput(); } function getFileSize(file) { let suffix = "bytes"; let size = file.size; if (size >= 1024 && size < 1024000) { size = Math.round(size / 1024 * 100) / 100; suffix = "KB"; } else if (size >= 1024000) { size = Math.round(size / 1024000 * 100) / 100; suffix = "MB"; } let fileSizeElement = document.createElement('span'); fileSizeElement.classList.add('fileUpload_fileSize'); fileSizeElement.innerHTML = `<strong>${size}</strong> ${suffix}`; return fileSizeElement; } } }());
Loader Loading Pseudo Element everything is ready, just needs the class names in place
.loading .locationsWrapper::before { content: ''; position: absolute; width: 100%; height: 100%; top: 0; left: 0; background: rgba(255, 255, 255, 0.8); z-index: 8; display: block; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' x='0px' y='0px' viewBox='0 0 54 100'%3E%3Ccircle fill='%23000' stroke='none' cx='6' cy='50' r='6'%3E%3Canimate attributeName='opacity' dur='1s' values='0;1;0' repeatCount='indefinite' begin='0.1'%3E%3C/animate%3E%3C/circle%3E%3Ccircle fill='%23000' stroke='none' cx='26' cy='50' r='6'%3E%3Canimate attributeName='opacity' dur='1s' values='0;1;0' repeatCount='indefinite' begin='0.2'%3E%3C/animate%3E%3C/circle%3E%3Ccircle fill='%23000' stroke='none' cx='46' cy='50' r='6'%3E%3Canimate attributeName='opacity' dur='1s' values='0;1;0' repeatCount='indefinite' begin='0.3'%3E%3C/animate%3E%3C/circle%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: center; background-size: 60px; }
Preloader where logo is in center center of page, then scales down to somewhere actually on the page seamlessly, other stuff transitions in too
function element_how_premium_video_preloader() { if (!is_front_page() ) { return; /* run on front page only */ } if (\Elementor\Plugin::$instance->editor->is_edit_mode() || \Elementor\Plugin::$instance->preview->is_preview_mode() ) { return; /* don't run while in the editor */ } ?> <div class="preloader_container"> <svg class="preloader_animated_logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 196 119.1"><path d="M10.1,114.1v1.7c0,2.1-1,3.3-3,3.3s-3-1.2-3-3.3v-6.7c0-2.1,1-3.3,3-3.3s3,1.2,3,3.3v1.3H8.2 v-1.4c0-0.9-0.4-1.3-1.1-1.3s-1.1,0.3-1.1,1.3v7c0,0.9,0.4,1.3,1.1,1.3s1.1-0.3,1.1-1.3v-1.8L10.1,114.1z"></path><path d="M20.8,109.1c0-2.1,1.1-3.3,3.1-3.3s3.1,1.2,3.1,3.3v6.7c0,2.1-1.1,3.3-3.1,3.3 s-3.1-1.2-3.1-3.3V109.1z M22.9,116c0,0.9,0.4,1.3,1.1,1.3s1.1-0.4,1.1-1.3v-7c0-0.9-0.4-1.3-1.1-1.3s-1.1,0.3-1.1,1.3V116z"></path><path d="M39.8,109.5L39.8,109.5l0,9.4h-1.8v-13h2.6l2.1,7.8h0V106h1.8v13h-2.1L39.8,109.5z"></path><path d="M58.2,105.8c2,0,3,1.2,3,3.3v0.4h-1.9v-0.5c0-0.9-0.4-1.3-1-1.3s-1,0.3-1,1.3 c0,0.9,0.4,1.7,1.7,2.8c1.7,1.5,2.2,2.6,2.2,4.1c0,2.1-1,3.3-3,3.3s-3-1.2-3-3.3V115h1.9v0.9c0,0.9,0.4,1.3,1.1,1.3 s1.1-0.3,1.1-1.3c0-0.9-0.4-1.6-1.7-2.8c-1.7-1.5-2.2-2.6-2.2-4.1C55.2,107,56.3,105.8,58.2,105.8"></path><path d="M71.5,106h6.3v1.8h-2.1v11.1h-2v-11.1h-2.1V106z"></path><path d="M92.6,118.9c-0.2-0.5-0.2-1.1-0.2-1.6v-2c0-1.2-0.4-1.7-1.3-1.7h-0.7v5.3h-2v-13h3.1 c2.1,0,3,1,3,3v1c0.1,1.1-0.4,2.1-1.3,2.6v0c1,0.4,1.3,1.4,1.3,2.8v2c0,0.5,0,1.1,0.2,1.6L92.6,118.9z M90.3,107.8v4h0.8 c0.8,0,1.2-0.3,1.2-1.4v-1.3c0-0.9-0.3-1.3-1-1.3L90.3,107.8z"></path><path d="M107.3,106v10c0,0.9,0.4,1.3,1,1.3s1.1-0.3,1.1-1.3v-10h1.9v9.9c0,2.1-1,3.3-3,3.3 s-3-1.2-3-3.3V106H107.3z"></path><path d="M128.3,114.1v1.7c0,2.1-1,3.3-3,3.3s-3-1.2-3-3.3v-6.7c0-2.1,1-3.3,3-3.3s3,1.2,3,3.3v1.3h-1.9 v-1.4c0-0.9-0.4-1.3-1.1-1.3s-1.1,0.3-1.1,1.3v7c0,0.9,0.4,1.3,1.1,1.3s1.1-0.3,1.1-1.3v-1.8L128.3,114.1z"></path><path d="M138.6,106h6.3v1.8h-2.1v11.1h-2v-11.1h-2.1L138.6,106z"></path><rect id="Rectangle_121" x="155.4" y="106" width="2" height="13"></rect><path d="M168.4,109.1c0-2.1,1.1-3.3,3.1-3.3s3.1,1.2,3.1,3.3v6.7c0,2.1-1.1,3.3-3.1,3.3 s-3.1-1.2-3.1-3.3V109.1z M170.5,116c0,0.9,0.4,1.3,1.1,1.3s1.1-0.4,1.1-1.3v-7c0-0.9-0.4-1.3-1.1-1.3s-1.1,0.3-1.1,1.3V116z"></path><path d="M187.4,109.5L187.4,109.5l0,9.4h-1.8v-13h2.6l2.1,7.8h0V106h1.8v13h-2.1L187.4,109.5z"></path><path d="M0,34.4c0-12.8,6.7-20.1,19.1-20.1s19.1,7.3,19.1,20.1v41.6c0,12.8-6.7,20.1-19.1,20.1 S0,88.7,0,75.9V34.4z M12.6,76.7c0,5.7,2.5,7.9,6.5,7.9s6.5-2.2,6.5-7.9V33.6c0-5.7-2.5-7.9-6.5-7.9s-6.5,2.2-6.5,7.9V76.7z"></path><path d="M49.5,11h-11V0h11V11z M46.5,15.2H59v79.9H46.5V15.2z M66.9,11h-11V0h11V11z"></path><path d="M84.8,63.2l-3.9,7.3v24.5H68.4V15.2h12.6v34.6h0.2l16.2-34.6h12.6L92.5,50.8l17.5,44.3H97 L84.8,63.2z"></path><path d="M114,34.4c0-12.8,6.7-20.1,19.1-20.1s19.1,7.3,19.1,20.1v41.6c0,12.8-6.7,20.1-19.1,20.1 S114,88.7,114,75.9V34.4z M126.6,76.7c0,5.7,2.5,7.9,6.5,7.9s6.5-2.2,6.5-7.9V33.6c0-5.7-2.5-7.9-6.5-7.9s-6.5,2.2-6.5,7.9V76.7z"></path><path d="M177.4,14.3c12.2,0,18.5,7.3,18.5,20.1v2.5H184v-3.3c0-5.7-2.3-7.9-6.3-7.9s-6.3,2.2-6.3,7.9 c0,5.8,2.5,10.2,10.7,17.3c10.5,9.2,13.8,15.9,13.8,25c0,12.8-6.4,20.1-18.7,20.1s-18.7-7.3-18.7-20.1V71h11.9v5.7 c0,5.7,2.5,7.8,6.5,7.8s6.5-2.1,6.5-7.8c0-5.8-2.5-10.2-10.7-17.3c-10.5-9.2-13.8-15.9-13.8-25C158.9,21.6,165.2,14.3,177.4,14.3"></path></svg> </div> <style> .preloader_logo_on_page { opacity: 0; } .preloader_nav_icon, .preloader_nav_item, .preloader_cta_on_page { opacity: 0; transition: opacity 0.6s; } .preloader_cta_on_page { transition: opacity 0.6s 1.9s; } .preloader_is_ready .preloader_nav_icon, .preloader_is_ready .preloader_nav_item, .preloader_is_ready .preloader_cta_on_page { opacity: 1; } .preloader_image_on_page::after, .preloader_heading_on_page::after { content: ''; position: absolute; width: 100%; height: 110%; top: -10%; left: 0; background-color: #221E1F; transition: transform 1.1s cubic-bezier(0.475, 0.000, 0.175, 1.000); transform-origin: 0% 100%; z-index: 4; } .preloader_is_ready .preloader_image_on_page::after { transform: scaleX(0); } .preloader_is_ready .preloader_heading_on_page::after { transform: scaleY(0); transition-delay: 0.9s; } .preloader_container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 9998; display: flex; justify-content: center; align-items: center; transition: opacity 0.6s; } .preloader_animated_logo { animation: fadeLogoIn 0.4s both; transform-origin: 0 0; transition: transform 1.16s cubic-bezier(0.475, 0.000, 0.175, 1.000); width: 300px; fill: #a6a097; z-index: 9998; } @keyframes fadeLogoIn { 0% { opacity: 0; } 100% { opacity: 1; } } </style> <script> (function () { document.documentElement.style.overflow = "hidden"; let shownDelayPassed = false; let abortPreloader = false; setTimeout(function () { shownDelayPassed = true; }, 700); /* if window.scrollY > 100, then remove the preloader and add class preloader_is_ready to the documentElement */ setTimeout(function () { if (window.scrollY > 100) { let preloader = document.querySelector(".preloader_container"); document.documentElement.classList.add("preloader_is_ready"); preloader.remove(); abortPreloader = true; document.documentElement.style.removeProperty('overflow'); setTimeout(function () { let preloader_logo = document.querySelector(".preloader_logo_on_page"); preloader_logo.style.opacity = "1"; }, 300); } }, 10); window.addEventListener("load", function () { if (abortPreloader) return; /* don't run the preloader if the preloader has been aborted */ let preloader = document.querySelector(".preloader_container"); let preloader_logo = document.querySelector(".preloader_logo_on_page"); let preloader_animated_logo = document.querySelector(".preloader_animated_logo"); let preloader_cta_on_page = document.querySelector(".preloader_cta_on_page"); if (shownDelayPassed) { animateLogo() document.documentElement.classList.add("preloader_is_ready"); } else { setTimeout(function () { animateLogo() document.documentElement.classList.add("preloader_is_ready"); }, 700); } function animateLogo() { /* calculate the exact position and width of the preloader_logo , then transform the preloader_animated_logo to the exact position and width */ let preloader_logo_position = preloader_logo.getBoundingClientRect(); let preloader_logo_width = preloader_logo_position.width; let preloader_logo_top = preloader_logo_position.top; let preloader_logo_left = preloader_logo_position.left; let preloader_animated_logo_position = preloader_animated_logo.getBoundingClientRect(); let preloader_animated_logo_width = preloader_animated_logo_position.width; let preloader_animated_logo_top = preloader_animated_logo_position.top; let preloader_animated_logo_left = preloader_animated_logo_position.left; /* calculate to get the scale of the preloader_animated_logo */ let preloader_animated_logo_scale = preloader_logo_width / preloader_animated_logo_width; /* transform the preloader_animated_logo to the exact position and width */ preloader_animated_logo.style.transform = "translate(" + (preloader_logo_left - preloader_animated_logo_left) + "px, " + (preloader_logo_top - preloader_animated_logo_top) + "px) scale(" + preloader_animated_logo_scale + ")"; } preloader_animated_logo.addEventListener("transitionend", function () { setTimeout(function () { preloader_logo.style.opacity = "1"; document.documentElement.style.removeProperty('overflow'); preloader.remove(); }, 600); }, { once: true }); }); }()); </script> <?php } add_action('wp_head', 'element_how_premium_video_preloader');
GSAP Morph SVG
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1602 140"> <style type="text/css"> .animatedSVG_styles{fill:none;stroke:#928B82;} </style> <path class="animatedSVG_styles animatedSVG_line_with_gaps" d="M287.2 78H-.2M589.7 77l-265.7.4M961.4 76.4l-334.4.5M1602 75.3l-603.5 1"/> <path class="animatedSVG_styles animatedSVG_residentiel" d="M324,77V41.1l-18.4-18.4l-18.4,18.4V77"/> <path class="animatedSVG_styles animatedSVG_batiment" d="M627 77V21.9l-37.3.1v55M626.5 39.5h-37m0 19h37" /> <path class="animatedSVG_styles animatedSVG_civil" d="M998.5,77v37.3l-37.1,0.1V77"/> <path class="animatedSVG_styles animatedSVG_residentiel_flat" d="M324,77L324,77h-18.4h-18.4l0,0"/> <path class="animatedSVG_styles animatedSVG_batiment_flat" d="M627,77L627,77h-37.3l0,0 M626.5,77h-37 M589.5,77h37"/> <path class="animatedSVG_styles animatedSVG_civil_flat" d="M998.5,77L998.5,77l-37.1,0.1V77"/> </svg> window.addEventListener('load', function () { gsap.registerPlugin(MorphSVGPlugin); let parentRadios = document.querySelectorAll('.jet-list-tree__parent input[type="radio"]'); /* select the "all" radio option */ let allRadioOption = document.querySelector('.jet-radio-list__input'); let residentialIcon = document.querySelector('.animatedSVG_residentiel'); let civilIcon = document.querySelector('.animatedSVG_civil'); let buildingIcon = document.querySelector('.animatedSVG_batiment'); let residentialLine = document.querySelector('.animatedSVG_residentiel_flat'); let civilLine = document.querySelector('.animatedSVG_civil_flat'); let buildingLine = document.querySelector('.animatedSVG_batiment_flat'); let animations = []; let animationDuration = 0.6; allRadioOption.addEventListener('change', (e) => { if (allRadioOption.checked) { console.log(animations); animations.forEach((animation) => { animation.reverse(); }); } }); parentRadios.forEach((radio, i) => { radio.addEventListener('change', (e) => { if (!radio.checked) return; animations.forEach((animation, index) => { if (animation.totalProgress() == 1 && i == index) return; }); animations.forEach((animation, index) => { if (animation.totalProgress() == 1 && i != index) animation.reverse(); }); if (animations[i] && animations[i].totalProgress() == 1) return; if (i == 0) { animations[i] = gsap.to(residentialLine, { duration: animationDuration, morphSVG: residentialIcon }); } else if (i == 1) { animations[i] = gsap.to(buildingLine, { duration: animationDuration, morphSVG: buildingIcon}); } else if (i == 2) { animations[i] = gsap.to(civilLine, { duration: animationDuration, morphSVG: civilIcon}); } }); }); }); /* with tabs instead */ window.addEventListener('load', function () { gsap.registerPlugin(MorphSVGPlugin); let residentialIcon = document.querySelector('.animatedSVG_residentiel'); let civilIcon = document.querySelector('.animatedSVG_civil'); let buildingIcon = document.querySelector('.animatedSVG_batiment'); let residentialLine = document.querySelector('.animatedSVG_residentiel_flat'); let civilLine = document.querySelector('.animatedSVG_civil_flat'); let buildingLine = document.querySelector('.animatedSVG_batiment_flat'); let animations = []; let animationDuration = 0.6; const tabsTitles = document.querySelectorAll('.e-n-tab-title'); tabsTitles.forEach((tabTitle, i) => { tabTitle.addEventListener('click', (e) => { animations.forEach((animation, index) => { if (animation.totalProgress() == 1 && i != index) animation.reverse(); }); if (animations[i] && animations[i].totalProgress() == 1) return; if (i == 0) { animations[i] = gsap.to(residentialLine, { duration: animationDuration, morphSVG: residentialIcon }); } else if (i == 1) { animations[i] = gsap.to(buildingLine, { duration: animationDuration, morphSVG: buildingIcon }); } else if (i == 2) { animations[i] = gsap.to(civilLine, { duration: animationDuration, morphSVG: civilIcon }); } }); }); /* animate redientialLine on page load */ tabsTitles[0].click(); });
Select2 Jet Smart Filters Radios
window.addEventListener('load', function () { $ = jQuery; let select2Instance; function formatOption(option) { if (!option.id) { return option.text; } var $state = $( `<span class="mobileSelect_optionWrapper"><span class="mobileSelect_SVG">${SVGs[+option.id]}</span>${option.text}<span class"mobileSelect_downChevron"></span></span>` ); return $state; }; let SVGs = [ ``, ` <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-geo-alt-fill" viewBox="0 0 16 16"> <path d="M8 1a7 7 0 0 1 7 7c0 3.182-2.118 6.182-7 10-4.882-3.818-7-6.818-7-10a7 7 0 0 1 7-7zm0 2a5 5 0 0 1 5 5c0 2.273-1.515 4.455-5 7.5-3.485-3.045-5-5.227-5-7.5a5 5 0 0 1 5-5z"/> </svg>`, ` <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-geo-alt-fill" viewBox="0 0 16 16"> <path d="M8 1a7 7 0 0 1 7 7c0 3.182-2.118 6.182-7 10-4.882-3.818-7-6.818-7-10a7 7 0 0 1 7-7zm0 2a5 5 0 0 1 5 5c0 2.273-1.515 4.455-5 7.5-3.485-3.045-5-5.227-5-7.5a5 5 0 0 1 5-5z"/> </svg>`, ` <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-geo-alt-fill" viewBox="0 0 16 16"> <path d="M8 1a7 7 0 0 1 7 7c0 3.182-2.118 6.182-7 10-4.882-3.818-7-6.818-7-10a7 7 0 0 1 7-7zm0 2a5 5 0 0 1 5 5c0 2.273-1.515 4.455-5 7.5-3.485-3.045-5-5.227-5-7.5a5 5 0 0 1 5-5z"/> </svg>`, ]; $(document).ready(function () { select2Instance = $('.mobileSelect').select2({ templateResult: formatOption, templateSelection: formatOption, }); let radios = document.querySelectorAll('.jet-radio-list__row.jet-filter-row:first-child input, .jet-radio-list__row.jet-filter-row.jet-list-tree__parent input'); console.log(radios); console.log(select2Instance); select2Instance.on('change', function (e) { let index = $(this).find('option:selected').index(); radios[index].click(); }); }); });
Image Changer Based On SVG Image Map, the image changes on hover of specific areas
I have the following HTML markup: <div class="e-con-inner"> <div class="elementor-element elementor-element-7ffd876 e-con-full e-flex e-con e-child" data-id="7ffd876" data-element_type="container" data-settings="{"content_width":"full","container_type":"flex"}"> <div class="elementor-element elementor-element-674a739 infoSelectImage infoSelectImageFirst elementor-widget elementor-widget-image" data-id="674a739" data-element_type="widget" data-widget_type="image.default"> <div class="elementor-widget-container"> <img width="1500" height="1554" src="https://templates.element.how/wp-content/uploads/2024/02/3032xpt-optimized.jpg" class="attachment-full size-full wp-image-46758" alt=""> </div> </div> <div class="elementor-element elementor-element-8ef6479 infoSelectImage elementor-widget elementor-widget-image" data-id="8ef6479" data-element_type="widget" data-widget_type="image.default"> <div class="elementor-widget-container"> <img decoding="async" width="1500" height="1554" src="https://templates.element.how/wp-content/uploads/2024/02/4032xpt-optimized.jpg" class="attachment-full size-full wp-image-46759" alt=""> </div> </div> <div class="elementor-element elementor-element-9227fde infoSelectImage elementor-widget elementor-widget-image" data-id="9227fde" data-element_type="widget" data-widget_type="image.default"> <div class="elementor-widget-container"> <img loading="lazy" decoding="async" width="1500" height="1554" src="https://templates.element.how/wp-content/uploads/2024/02/5032xpt-optimized.jpg" class="attachment-full size-full wp-image-46757" alt=""> </div> </div> <div class="elementor-element elementor-element-3b9d9a8 infoSelectHTML elementor-widget elementor-widget-html" data-id="3b9d9a8" data-element_type="widget" data-widget_type="html.default"> <div class="elementor-widget-container"> <svg class="infoSelectSVG" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1500 1554"> <a xlink:href="#info-select-2"><polygon points="1288,1422 1148,1434 1168,1319 1030,1221 1030,1159 1294,1305 1288,1422"></polygon></a> <a xlink:href="#info-select-3"><polygon points="1044,1302 1056,1439 1148,1434 1168,1319 1030,1221 1044,1302"></polygon></a> </svg> <style> /* SVG map created with https://n-peugnet.github.io/image-map-creator/ , width and height properties changed to viewbox, hrefs added */ .infoSelectImage:not(.infoSelectImageFirst) { position: absolute; display: none; } .infoSelectHTML, .infoSelectSVG { position: absolute; width: 100%; height: 100%; fill: transparent; left: 0; top: 0; z-index: 8 } .infoSelectLearnMore { display: none; } .infoSelectInfoBox { padding: 20px; } .infoSelectInfoBox:hover, .infoSelectInfoBox.isHovered { background-color: #eee; } .infoSelectInfoBox.isActive .infoSelectLearnMore { display: block; } </style> <script> document.addEventListener('DOMContentLoaded', function () { let listenersEnabled = true; const svgElements = document.querySelectorAll('.infoSelectSVG a'); const infoBoxes = document.querySelectorAll('.infoSelectInfoBox'); const images = document.querySelectorAll('.infoSelectImage'); images[0].classList.add('isShown'); const removeHoverListeners = () => { if (listenersEnabled) { svgElements.forEach((element, index) => { const enterFn = element.__mouseenter; const leaveFn = element.__mouseleave; if (enterFn) { element.removeEventListener('mouseenter', enterFn); } if (leaveFn) { element.removeEventListener('mouseleave', leaveFn); } }); infoBoxes.forEach(box => box.classList.remove('isHovered')); listenersEnabled = false; } }; svgElements.forEach((element, index) => { const mouseEnterFunction = () => { if (listenersEnabled) { if (infoBoxes[index]) { infoBoxes[index].classList.add('isHovered'); } if (images[index]) { images[index].classList.add('isShown'); } } }; const mouseLeaveFunction = () => { if (listenersEnabled) { if (infoBoxes[index]) { infoBoxes[index].classList.remove('isHovered'); } if (images[index]) { images[index].classList.remove('isShown'); images[0].classList.add('isShown'); } } }; element.__mouseenter = mouseEnterFunction; element.__mouseleave = mouseLeaveFunction; element.addEventListener('mouseenter', mouseEnterFunction); element.addEventListener('mouseleave', mouseLeaveFunction); element.addEventListener('click', (e) => { e.preventDefault(); removeHoverListeners(); // Remove isActive class from all infoBoxes and images infoBoxes.forEach(box => box.classList.remove('isActive')); images.forEach(img => img.classList.remove('isShown')); // Add isActive class to the related infoBox if (infoBoxes[index]) { infoBoxes[index].classList.add('isActive'); } // Add isShown class to the related image if (images[index]) { images[index].classList.add('isShown'); } }); }); infoBoxes.forEach((box, index) => { box.addEventListener('click', () => { removeHoverListeners(); // Remove isActive class from all infoBoxes and images infoBoxes.forEach(box => box.classList.remove('isActive')); images.forEach(img => img.classList.remove('isShown')); // Add isActive class to the clicked infoBox box.classList.add('isActive'); // Add isShown class to the related image if (images[index]) { images[index].classList.add('isShown'); } }); }); }); </script>
Elementor WordPress Menu element, center items
.elementor-nav-menu--layout-horizontal .elementor-nav-menu.elementor-nav-menu { flex-wrap: nowrap; justify-content: space-between; width: 1140px; } .custom-menu .menu-item.menu-item { flex: 1; } .custom-menu .menu-item.menu-item > a { justify-content: center; } .elementor-nav-menu--layout-horizontal .elementor-nav-menu.elementor-nav-menu.elementor-nav-menu>li>a { margin:0; } .elementor-nav-menu.elementor-nav-menu::after { content:none; }
Elementor Form Error message translation
Elementor Form Error message translation /* from https://www.facebook.com/groups/916625091796611/ */ To translate the error message, here for example Spanish, add html widget, and paste this following code in it: <script> document.addEventListener("DOMContentLoaded", function() { var elements = document.getElementsByTagName("INPUT"); for (var i = 0; i < elements.length; i++) { elements[i].oninvalid = function(e) { e.target.setCustomValidity(""); if (!e.target.validity.valid) { e.target.setCustomValidity("Campo obligatorio"); } }; elements[i].oninput = function(e) { e.target.setCustomValidity(""); }; } }) </script> you can change the what's written between brackets on the following line(inside the apostrophe sign): 'e.target.setCustomValidity'
Milan Post Layout with categories and sub category hierarchy
function products_archive_shortcode_func() { if(isset($_GET['elementor-preview'])) { } else { ow_categories_with_subcategories_and_posts( 'categorie-produits', 'produits'); } } add_shortcode('products_archive_shortcode', 'products_archive_shortcode_func'); /* modified from https://wordpress.stackexchange.com/questions/270176/showing-categories-and-subcategories-with-posts */ function ow_categories_with_subcategories_and_posts( $taxonomy, $post_type ) { $taxonomy = $taxonomy; $post_type = $post_type; $type_of_client = isset($_GET['display']) ? $_GET['display'] : 'particuliers'; // Get the top categories that belong to the provided taxonomy (the ones without parent) $categories = get_terms( array( 'taxonomy' => $taxonomy, 'parent' => 0, // <-- No Parent 'orderby' => 'term_id', 'hide_empty' => true // <-- change to false to also display empty ones ) ); ?> <div> <?php // Iterate through all categories to display each individual category foreach ( $categories as $category ) { $cat_name = $category->name; $cat_id = $category->term_id; $cat_slug = $category->slug; // Display the name of each individual category echo '<h3 class="listingParentCategory" role="button" tabindex="0" aria-expanded="false">' . $cat_name . '</h3>'; // Get all the subcategories that belong to the current category $subcategories = get_terms( array( 'taxonomy' => $taxonomy, 'parent' => $cat_id, // <-- The parent is the current category 'orderby' => 'term_id', 'hide_empty' => true ) ); ?> <div class="listingContent" hidden> <?php // Iterate through all subcategories to display each individual subcategory foreach ( $subcategories as $subcategory ) { $subcat_name = $subcategory->name; $subcat_id = $subcategory->term_id; $subcat_slug = $subcategory->slug; // Get all posts that belong to this specific subcategory $posts = new WP_Query( array( 'post_type' => $post_type, 'posts_per_page' => -1, // <-- Show all posts 'hide_empty' => true, 'order' => 'ASC', 'tax_query' => array( 'relation' => 'AND', array( 'taxonomy' => $taxonomy, 'terms' => $subcat_id, 'field' => 'id' ), array( 'taxonomy' => 'categories-clients', 'terms' => $type_of_client, 'field' => 'slug' ) ) ) ); // If there are posts available within this subcategory if ( $posts->have_posts() ): // Display the name of each individual subcategory with ID and Slug // if $subcat_name == 'N/A', then don't display the subcategory if ($subcat_name != 'N/A') { echo '<h4 class="listingSubCategory">' . $subcat_name . '</h4>'; } ?> <div class="listingProductsWrapper"> <?php // As long as there are posts to show while ( $posts->have_posts() ): $posts->the_post(); //Show the title of each post with the Post ID ?> <a class="listingProduct" href="<?php the_permalink(); ?>"><?php the_title(); ?></a> <?php endwhile; ?> </div> <?php endif; wp_reset_query(); } ?> </div> <?php } ?> </div> <?php }
Elementor List Icon element to Anchor Links for the Horizontal Scroll Containers
const observer = new IntersectionObserver((entries, observer) => { let mostVisible = entries.reduce((prev, current) => (prev.intersectionRatio > current.intersectionRatio) ? prev : current); const links = document.querySelectorAll('ul.elementor-icon-list-items.elementor-inline-items a'); links.forEach(link => link.classList.remove('isActive')); if (mostVisible.intersectionRatio > 0.6) { const index = Array.from(document.querySelectorAll('.horizontalScrollTranslate > .e-con:not(:first-child)')).indexOf(mostVisible.target); const activeLink = links[index]; activeLink.classList.add('isActive'); } }, { threshold: 0.6 }); const elements = document.querySelectorAll('.horizontalScrollTranslate > .e-con:not(:first-child)'); elements.forEach(el => observer.observe(el));
Simple Offset Mosaic Grid from a Normal 3x3 grid (desktop) 2x2 tablet
.jet-listing-grid__item.jet-listing-grid__item:nth-child(3n + 2) { position: relative; top: 200px; } @media (max-width:1024px) { .jet-listing-grid__item.jet-listing-grid__item:nth-child(3n + 2) { position: relative; top: 0px; } .jet-listing-grid__item.jet-listing-grid__item:nth-child(2n) { position: relative; top: 150px; } } @media (max-width:767px) { .jet-listing-grid__item.jet-listing-grid__item:nth-child(2n) { position: relative; top: 0; } }
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(); }); }); } });
Make Elementor Divider Elementor Vertical Instead
/* to make the Divider element vertical */ selector.imageRevealDivider { width: 21px; /* set same as "Size" setting in Divider element's styles tab */ /* no need to change anything below this line */ max-width:initial; height: 100%; overflow: hidden; } selector.imageRevealDivider.imageRevealDivider .elementor-widget-container { transform:rotate(90deg) translateY(-100%); transform-origin: 0 0; height: auto; width: 100vh; } /* END OF to make the Divider element vertical */
Add a logo to the elementor native page transitions (or any image)
e-page-transition::before { content: ''; width: 200px; height: 200px; background-image: url(https://staging.vntr.ch/wp-content/uploads/logo-vntr-white.svg); background-repeat:no-repeat; background-position: center center; background-size:200px 200px; }
Background video that has great performance because it doesn't wait for Elementor JS to load before starting loading
For this there is really only one way, and even then it's not a perfect fix: self hosting. See my tutorial here: https://element.how/self-hosting-videos-bunnycdn/ Self hosting a very well compressed mp4 is what will make it play the earliest on page load. However even then there is another challenge: If you use Elementor to set your background videos, the video won't start loading until after all the page (and the JS) are loading and executed! Elementor adds the <video> through JS, so it only gets added after the page is fully loaded. That's probably why you found it impossible to get a good score. So, the fix is to use the HTML element instead. Add it first thing in the container that should have the background video. Then add this in it: <style> .customBackgroundVideo.customBackgroundVideo, .customBackgroundVideo .elementor-widget-container, .customBackgroundVideo video{ position: absolute; height: 100%; width: 100%; top: 0; left: 0; overflow: hidden; z-index: 0; object-fit: cover; } </style> <video autoplay muted playsinline loop src="https://templates.element.how/wp-content/uploads/2022/05/gears_webready.mp4"></video> Then add the class name customBackgroundVideo to the HTML element under Advanced > CSS classes. And change the URL to your own video. That's the fastest way the video will load! If that still doesn't satisfy Google PSI, then don't use videos above the fold.
Trendy dot matrix pattern design as pseudo element
footer::before { --dot-color: #333; --dot-bg-color: transparent; --dot-size: 4px; --dot-spacing: 40px; --width-f: 10; --height-f: 10; --top-pos: 30px; --left-pos: 30px; content: ""; position: absolute; width: calc(var(--dot-spacing) * var(--width-f)); height: calc(var(--dot-spacing) * var(--height-f)); left: var(--left-pos); top: var(--top-pos); background-image: radial-gradient( var(--dot-color) var(--dot-size), var(--dot-bg-color) 0 ); background-size: var(--dot-spacing) var(--dot-spacing); } /* above if for circle dots, below is for square dots */ /* modified from https://codepen.io/edmundojr/pen/xOYJGw */ footer::before { --dot-color: #333; --dot-bg-color: #fff; --dot-size: 14px; --dot-spacing: 40px; --width-f: 10; --height-f: 10; --top-pos: 30px; --left-pos: 30px; content: ""; position: absolute; width: calc(var(--dot-spacing) * var(--width-f)); height: calc(var(--dot-spacing) * var(--height-f)); left: var(--left-pos); top: var(--top-pos); background: linear-gradient(90deg, var(--dot-bg-color) calc(var(--dot-spacing) - var(--dot-size)), transparent 1%) center / var(--dot-spacing) var(--dot-spacing), linear-gradient(var(--dot-bg-color) calc(var(--dot-spacing) - var(--dot-size)), transparent 1%) center / var(--dot-spacing) var(--dot-spacing), var(--dot-color); }
wooCommerce Update Bricks Product Listing after Filtering (here, from jet smart filters, should work with others)
window.addEventListener('load', function () { let debounceTimer; const debounceDelay = 300; const observer = new MutationObserver((mutations) => { const hasChildListMutation = mutations.some(mutation => mutation.type === "childList"); if (hasChildListMutation) { if (debounceTimer) clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { updateWooAfterAjaxFilters(); }, debounceDelay); } }); const config = { childList: true, subtree: false }; const targetNode = document.querySelector(".brx-grid"); observer.observe(targetNode, config); function updateWooAfterAjaxFilters() { jQuery('.variations_form').each(function () { jQuery(this).wc_variation_form(); }); bricksWooQuantityTriggersFn.run() bricksWooLoopQtyListenerFn.run() bricksWooAjaxAddToCartFn.run() } });
WooCommerce add increase and decrease Qty button in the Single Product and Cart page (maybe product archive too) , and in single product and cart, add text after the Qty input field through an ACF custom field
.qtyButtonsWrapper { display: flex; flex-direction: column; gap:3px; } .quantity.has-custom-text { display: flex; align-items: center; gap:3px; position: relative; width: fit-content; } .qtyButtonsWrapper.qtyButtonsWrapper.qtyButtonsWrapper.qtyButtonsWrapper.qtyButtonsWrapper > button { border: 1px solid #333; border: none; color: #333; font-weight: 900; font-family: arial; font-size: 18px; aspect-ratio: 1; border-radius:0; padding: 10px; line-height: 0; margin: 0; } /* hovering */ .qtyButtonsWrapper.qtyButtonsWrapper.qtyButtonsWrapper.qtyButtonsWrapper.qtyButtonsWrapper > button:hover { color: #fff; background-color:#333; } input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } .product-quantity-text { position: absolute; right: 13px; } .quantity.has-custom-text .input-text.qty.qty.qty.qty.qty { min-width: 84px; text-align: left; } <?php add_action("wp_footer", "add_custom_quantity_text_data_attribute"); function add_custom_quantity_text_data_attribute() { // Check if we're on a product page if (!is_product()) { return; } $productQuantityText = esc_js( get_post_meta(get_the_ID(), "product_quantity_text", true) ); ?> <script> (function() { let productQuantityText = "<?php echo $productQuantityText; ?>"; let qtyInputs = document.querySelectorAll('.elementor-add-to-cart .quantity'); qtyInputs.forEach(function(input) { input.setAttribute('data-product-quantity-text', productQuantityText); }); })(); </script> <?php } add_filter('woocommerce_cart_item_quantity', 'customize_cart_item_quantity_with_data_attribute', 10, 3); function customize_cart_item_quantity_with_data_attribute($product_quantity, $cart_item_key, $cart_item) { // Access your custom field value here. Adjust 'product_quantity_text' to your actual custom field key. $quantity_text = get_post_meta($cart_item['product_id'], 'product_quantity_text', true); // Inject the data attribute into the quantity div instead of the input field $dom = new DOMDocument(); @$dom->loadHTML(mb_convert_encoding($product_quantity, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); $quantityDivs = $dom->getElementsByTagName('div'); foreach ($quantityDivs as $div) { if ($div->getAttribute('class') === 'quantity') { $div->setAttribute('data-product-quantity-text', $quantity_text); break; // Assuming only one quantity div per product in cart item } } return $dom->saveHTML(); } document.addEventListener("DOMContentLoaded", function () { function updateQuantityFields() { document.querySelectorAll(".quantity:not(.has-custom-text)").forEach(function (quantityDiv) { quantityDiv.classList.add("has-custom-text"); const qtyButtonWrapper = document.createElement("div"); qtyButtonWrapper.className = "qtyButtonsWrapper"; const decreaseButton = document.createElement("button"); decreaseButton.textContent = "–"; decreaseButton.type = "button"; decreaseButton.className = "decreaseQty"; const increaseButton = document.createElement("button"); increaseButton.textContent = "+"; increaseButton.type = "button"; increaseButton.className = "increaseQty"; qtyButtonWrapper.appendChild(increaseButton); qtyButtonWrapper.appendChild(decreaseButton); quantityDiv.insertBefore(qtyButtonWrapper, quantityDiv.firstChild); const customTextDiv = document.createElement("div"); customTextDiv.className = "product-quantity-text"; quantityDiv.appendChild(customTextDiv); customTextDiv.textContent = "Bags"; // Replace with dynamic value const productQuantityText = quantityDiv.getAttribute( "data-product-quantity-text" ); if (productQuantityText) { customTextDiv.textContent = productQuantityText; } }); } updateQuantityFields(); jQuery(document.body).on('updated_wc_div', function () { updateQuantityFields(); }); let updateCartTimeout; document.body.addEventListener("click", function (e) { if ( e.target.classList.contains("increaseQty") || e.target.classList.contains("decreaseQty") ) { const quantityField = e.target.closest(".quantity").querySelector(".qty"); let currentValue = parseInt(quantityField.value, 10); if (e.target.classList.contains("increaseQty")) { currentValue++; } else if (currentValue > 1) { /* Prevent quantity less than 1 */ currentValue--; } quantityField.value = currentValue; const changeEvent = new Event("change"); quantityField.dispatchEvent(changeEvent); let cartUpdateButton = document.querySelector('button[name=update_cart]'); if (cartUpdateButton) cartUpdateButton.disabled = false; clearTimeout(updateCartTimeout); updateCartTimeout = setTimeout(() => { let cartUpdateButton = document.querySelector('button[name=update_cart]'); if (cartUpdateButton) cartUpdateButton.click(); } , 1400); } }); });
Swiper with thumbnail slides below in another slider
selector .swiper-slide { cursor: pointer; } selector .swiper-slide-image { height: 300px; width: 100%; object-fit: cover; } selector .swiper-slide-image { opacity: 0.7; transition: opacity 0.4s; } selector .swiper-slide-active .swiper-slide-image { opacity: 1; } let carouselControlDebugObj = { initFunctions: {}, forEachSwiperNames: {}, setupEachInstance: {}, init: {} }; window.addEventListener('load', function () { function initSwiperControl() { let swipers, actualSwipers, visibleSwipers; let time = 0; function whenReady() { try { swipers = document.querySelectorAll('[data-control-swiper] .swiper-container, [data-control-swiper] .swiper'); actualSwipers = [...swipers].filter(e => e.closest('.elementor-sticky__spacer') == undefined); /* Make compatible with Elementor Sticky feature */ visibleSwipers = actualSwipers.filter(e => e.offsetHeight > 0); /* Filter out swipers that are not on the page */ carouselControlDebugObj.init.swipers = swipers; carouselControlDebugObj.init.actualSwipers = actualSwipers; carouselControlDebugObj.init.visibleSwipers = visibleSwipers; carouselControlDebugObj.init.visibleSwipersSwiperObjects = visibleSwipers.map(e => e.swiper); if (visibleSwipers[visibleSwipers.length - 1].swiper == undefined) { throw 'error'; } init(); } catch (error) { if (time > 20000) { return false; } else { time += 250; setTimeout(whenReady, 250); console.log(error); } } } whenReady(); function init() { let miniSwiper = document.querySelector('.swiper-mini .swiper-container, .swiper-mini .swiper'); if (miniSwiper) { let swiperParams = miniSwiper.swiper.passedParams; carouselControlDebugObj.initFunctions.miniSwiper = miniSwiper; carouselControlDebugObj.initFunctions.swiperParams = swiperParams; miniSwiper.swiper.destroy(); new Swiper(miniSwiper, { ...swiperParams, slideToClickedSlide: true, centeredSlides: true, on: { slideChange: function () { }, slideChangeTransitionEnd: function () { }, init() { this.slides.forEach(slide => { slide.removeAttribute("inert"); slide.removeAttribute("aria-hidden"); }); }, }, }); } let allSwipers = document.querySelectorAll('[data-control-swiper] .swiper-container, [data-control-swiper] .swiper'); let swiperNames = []; allSwipers.forEach(function (swiperContainerElem) { swiperNames.push(swiperContainerElem.closest('[data-control-swiper]').getAttribute('data-control-swiper')); }); /* get unique swiper names */ swiperNames = [...new Set(swiperNames)]; carouselControlDebugObj.initFunctions.allSwipers = allSwipers; carouselControlDebugObj.initFunctions.swiperNames = swiperNames; swiperNames.forEach(swiperName => { let initSwipers = Array.from(allSwipers).filter(e => e.closest('[data-control-swiper]').getAttribute('data-control-swiper') == swiperName && e.swiper != undefined); carouselControlDebugObj.forEachSwiperNames[swiperName] = initSwipers; initSwipers.forEach((sw,i) => { setupEachInstance(sw, i); }) let enabled = true; let redirectionDelay; let preventJSloop = 0; function makeItTrue() { enabled = true; } function startRedirectionDelay() { redirectionDelay = setTimeout(makeItTrue, 230); } function resetRedirectionDelay() { clearTimeout(redirectionDelay); } function manageEnabled(from) { if (from == "forward" || from == "backward" || from == "specific" || from == "directcommand") { enabled = false; } setTimeout(function () { enabled = false; resetRedirectionDelay(); startRedirectionDelay(); preventJSloop = 0; }, 40); } function setupEachInstance(swiperContainerElem, i) { let params = new function () { this.events = ["keyup", "click"]; this.swiperInstance = swiperContainerElem.swiper; this.instanceId = swiperContainerElem.closest('[data-control-swiper]').getAttribute('data-control-swiper'); this.length = this.swiperInstance.slides.filter(e => !e.classList.contains('swiper-slide-duplicate')).length; this.relatedSwipers = Array.from(document.querySelectorAll(`[data-control-swiper="${this.instanceId}"] .swiper-container, [data-control-swiper="${this.instanceId}"] .swiper`)).filter(e => e != swiperContainerElem); this.relatedSwipersLength = this.relatedSwipers.length; this.swiperIsLooping = this.swiperInstance.params.loop; }; carouselControlDebugObj.setupEachInstance[i] = params; swiperContainerElem.classList.add('sp_rdy'); setupIndexChangeEvent(); function setupIndexChangeEvent() { params.swiperInstance.on('realIndexChange', function () { params.relatedSwipers.forEach((relatedSwiper) => { let swiperInner = relatedSwiper.swiper; if (enabled && params.swiperInstance.realIndex != swiperInner.realIndex && preventJSloop < params.relatedSwipersLength) { console.log('related swipers event after checks'); preventJSloop++; console.log(preventJSloop); if (params.swiperIsLooping) { if ( /* params.swiperInstance.swipeDirection == 'next' || */swiperInner.realIndex + 2 > params.length && 0 == params.swiperInstance.realIndex || swiperInner.realIndex + 1 == params.swiperInstance.realIndex) { manageEnabled("directCommand"); swiperInner.slideNext(); } else if ( /* params.swiperInstance.swipeDirection == 'prev' || */swiperInner.realIndex - 2 < 0 && params.length - 1 == params.swiperInstance.realIndex || swiperInner.realIndex - 1 == params.swiperInstance.realIndex) { manageEnabled("directCommand"); swiperInner.slidePrev(); } else { swiperInner.slideToLoop(params.swiperInstance.realIndex); manageEnabled(); } } else { swiperInner.slideTo(params.swiperInstance.activeIndex); manageEnabled(); } } }); }); } }; }); } } initSwiperControl(); });
ACF Repeater Gallery Shortcode [acf_repeater_gallery] where hovering titles changes the images, repeater has text and image field in it, image field needs to be set to array
function acf_repeater_gallery_shortcode() { // Check if the ACF plugin is active and the repeater field exists if (function_exists('have_rows') && have_rows('images_repeater')) { $output = '<section class="gallerySection">'; $output .= '<div class="galleryTitlesWrapper">'; $counter = 1; // Loop through the repeater rows while (have_rows('images_repeater')) { the_row(); $title = get_sub_field('title'); // Add 'isActive' class to the first gallery title $title_classes = 'galleryTitle'; if ($counter === 1) { $title_classes .= ' isActive'; } // Add title HTML with dynamic classes $output .= '<div class="' . $title_classes . '">'; $output .= '<span class="galleryCount">' . sprintf("%02d", $counter) . '</span> '; $output .= '<span class="galleryTitleText">' . esc_html($title) . '</span>'; $output .= '</div>'; $counter++; } $output .= '</div><div class="galleryImagesWrapper">'; // Reset the loop to output the images $counter = 1; while (have_rows('images_repeater')) { the_row(); $title = get_sub_field('title'); $image = get_sub_field('image'); // Check if the image field returns an array if (is_array($image)) { $image_id = $image['ID']; $image_classes = 'galleryImage'; // Add 'isActive' class to the first image if ($counter === 1) { $image_classes .= ' isActive'; } // Use wp_get_attachment_image to get the HTML for the image $image_html = wp_get_attachment_image($image_id, 'full', false, array('class' => $image_classes, 'alt' => esc_attr($title))); $output .= $image_html; } $counter++; } $output .= '</div></section>'; return $output; } return 'No gallery found'; } add_shortcode('acf_repeater_gallery', 'acf_repeater_gallery_shortcode'); [acf_repeater_gallery] <script> document.addEventListener('DOMContentLoaded', function () { const galleryTitles = document.querySelectorAll('.galleryTitle'); const galleryImages = document.querySelectorAll('.galleryImage'); galleryTitles.forEach((title, index) => { title.addEventListener('mouseenter', function () { galleryTitles.forEach(t => t.classList.remove('isActive')); title.classList.add('isActive'); galleryImages.forEach(img => img.classList.remove('isActive')); if (galleryImages[index]) { galleryImages[index].classList.add('isActive'); } }); }); }); </script> .gallerySection { display: flex; align-items: center; height: 100vh; /* height of the gallery */ --text-color:#999999; --text-color-active: #333333; } .galleryTitlesWrapper { width: 40%; max-width: 630px; flex-shrink: 0; display: flex; flex-direction: column; flex-grow: 1; padding: 0 78px; } .galleryTitle { display: flex; font-size: 1.2rem; padding: 0.3rem; border-bottom: 1px solid var(--text-color); font-weight: 600; cursor: pointer; color: var(--text-color); } .galleryCount { padding-right: 90px; } .galleryTitle.isActive { border-color: var(--text-color-active); color: var(--text-color-active); } .galleryImagesWrapper { position: relative; width: 100%; height: 100%; margin-right: 78px; } .galleryImage.galleryImage { position: absolute; width: 90%; height: 100%; object-fit: contain; object-position: left center; top: 0; left: 0; pointer-events: none; opacity: 0; transition: opacity 0.6s; } .galleryImage.galleryImage.isActive { opacity: 1; pointer-events: all; } @media (max-width:1023px){ .gallerySection { flex-direction: column; align-items: initial; height: auto; /* height of the gallery */ } .galleryTitlesWrapper { width: 100%; max-width: initial; padding: 0 78px 20px; } .galleryTitle { font-size: 1.2rem; padding: 0.3rem; } .galleryCount { padding-right: 90px; } .galleryImagesWrapper { height: 50vh; margin-right: initial; } .galleryImage.galleryImage { width: 100%; object-position: center top; } } @media (max-width:767px){ .galleryTitlesWrapper { padding: 90px 18px 10px; } .galleryTitle { font-size: 1.1rem; padding: 0.3rem; } .galleryCount { padding-right: 60px; } .galleryImagesWrapper { height: 50vh; } }
Elementor Flip Box Show Full On Mobile for better UX
@media (max-width: 767px) { .elementor-flip-box__layer.elementor-flip-box__layer.elementor-flip-box__layer.elementor-flip-box__layer { position: static; width: initial; height: initial; min-height: 250px; transform: none; display: flex; align-items: center; } .elementor-flip-box.elementor-flip-box.elementor-flip-box.elementor-flip-box.elementor-flip-box { height: auto; } }
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; }
Have a container and its content (probably an image) overflow the boxed container width on the right, while being responsive and considering the boxed width
selector { margin-right: clamp(-100px, (var(--content-width) + 50px - 100vw) / 2 , 0px); width: clamp(50%, (50% + (100vw - var(--content-width)) / 2) - 100px , 50% + 100px); }
Elementor New Mega Menu element overlay when nav is opened, active nav item styles when hovering related sub nav
/* background coming in from the top for hovered / active menu item */ selector .e-n-menu>.e-n-menu-wrapper>.e-n-menu-heading>.e-n-menu-title:not(.e-current)::before { content: ''; background-color: var(--e-global-color-b92eed0); position: absolute; width: 100%; height: 100%; left: 0; top: 0; z-index: -3; transition: 0.6s transform; transform: scaleY(0); transform-origin: 50% 0; } /* show on hover */ selector .e-n-menu>.e-n-menu-wrapper>.e-n-menu-heading>.e-n-menu-title:hover:not(.e-current)::before { transform: scaleY(1); } /* show on related sub nav active */ selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(1):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(1)::before, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(2):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(2)::before, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(3):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(3)::before, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(4):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(4)::before, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(5):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(5)::before, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(6):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(6)::before, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(7):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(7)::before, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(8):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(8)::before, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(9):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(9)::before, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(10):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(10)::before { transform: scaleY(1); } /* keep the hover color on related sub nav active */ selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(1):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(1) a, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(2):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(2) a, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(3):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(3) a, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(4):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(4) a, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(5):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(5) a, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(6):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(6) a, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(7):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(7) a, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(8):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(8) a, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(9):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(9) a, selector .e-n-menu:has(.e-n-menu-content > .e-con:nth-child(10):hover) .e-n-menu-heading>.e-n-menu-title:nth-child(10) a { color: var(--n-menu-title-color-hover); } /* version with :has, so that the overlay is on top of everything except the elements in the header, header container needs z-index 9999 */ /* dark overlay when sub navs are opened */ selector { z-index: 9999; } selector::after { content: ''; position: absolute; width: 100%; height: 100vh; background-color: #000000; top: 100%; left: 0; z-index: -4; pointer-events: none; opacity: 0; transition: opacity 0.6s; } selector:has(.e-n-menu-content > .e-active)::after { opacity: 0.7; /* adjust opacity of overlay here */ }
Elementor New Mega Menu element overlay when nav is opened, close menu on click outside
/* overlay when sub nav are opened */ .e-n-menu-content::before { content:''; position: absolute; width: 100%; height:200vh; background-color:#000000; top:0; z-index:-4; pointer-events:none; opacity:0; transition: opacity 0.3s; } .e-n-menu-content.e-active::before { opacity:0.7; } /* version with :has, so that the overlay is on top of everything except the elements in the header, header container needs z-index 9999 */ /* this version also supports clicking outside closes the menu */ /* overlay when sub nav are opened */ selector::after { content:''; position: absolute; width: 100%; height:200vh; background-color:#000000; top:-100vh; left: 0; z-index:-4; pointer-events:none; opacity:0; transition: opacity 0.3s; } selector:has(.e-n-menu-content.e-active)::after { opacity:0.7; pointer-events:all; } .e-n-menu-content.e-active { pointer-events: none; } .e-n-menu-content.e-active > *{ pointer-events: all ; } /* close the menu on click outside */ document.addEventListener('click', function(event) { const dropdownIcon = document.querySelector('.e-n-menu-dropdown-icon[aria-expanded="true"]'); const activeContent = event.target.closest('.e-n-menu-content.e-active'); const clickedMenuItem = event.target.closest('.e-n-menu-title'); if (dropdownIcon && !activeContent && !clickedMenuItem) { dropdownIcon.click(); } });
Elementor New Mega Menu element up chevron icon
/* up chevron pointing to parent menu item , add to "Item 1" etc */ selector::before { content: ''; width: 30px; height: 30px; position: absolute; background-color: var( --e-global-color-primary ); z-index: -3; transform: rotate(45deg); left: 30px; top:-4px; }
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>
Animated Copy Paste Button Check
$svgIcon = '<svg version="1.1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M12,5 L12,3.99791312 C12,2.89826062 12.897616,2 14.0048815,2 L14.9951185,2 C16.1061002,2 17,2.89449617 17,3.99791312 L17,5 L19.0044225,5 C19.555163,5 20,5.44724809 20,5.99895656 L20,7.00104344 C20,7.55733967 19.5542648,8 19.0044225,8 L9.9955775,8 C9.44483697,8 9,7.55275191 9,7.00104344 L9,5.99895656 C9,5.44266033 9.44573523,5 9.9955775,5 L12,5 L12,5 Z M11.0000005,4 L10.0027601,4 C8.89828672,4 8.00262776,4.89128252 8.00000577,6 L6.99742191,6 C5.89427625,6 5,6.88976324 5,8.00359486 L5,28.9964051 C5,30.10296 5.89092539,31 6.99742191,31 L22.0025781,31 C23.1057238,31 24,30.1102368 24,28.9964051 L24,8.00359486 C24,6.89703997 23.1090746,6 22.0025781,6 L20.9999942,6 L20.9999942,6 C20.9973726,4.89497907 20.1048269,4 18.9972399,4 L17.9999995,4 C17.9990959,2.34299141 16.6497738,1 14.9907455,1 L14.0092545,1 C12.3478441,1 11.0008998,2.33812603 11.0000005,4 L11.0000005,4 L11.0000005,4 Z M20.9999942,7 L22.0000398,7 C22.5452911,7 23,7.44908998 23,8.00307055 L23,28.9969294 C23,29.5610822 22.5523026,30 22.0000398,30 L6.9999602,30 C6.45470893,30 6,29.55091 6,28.9969294 L6,8.00307055 C6,7.43891776 6.44769743,7 6.9999602,7 L8.00000579,7 C8.00262739,8.10502093 8.89517314,9 10.0027601,9 L18.9972399,9 C20.1017133,9 20.9973722,8.10871748 20.9999942,7 L20.9999942,7 L20.9999942,7 Z M14.5,5 C14.7761424,5 15,4.77614239 15,4.5 C15,4.22385761 14.7761424,4 14.5,4 C14.2238576,4 14,4.22385761 14,4.5 C14,4.77614239 14.2238576,5 14.5,5 L14.5,5 Z M13.253553,23.6852821 Z"/><path d="M 14 23 L 21.0317 15.9071 L 20.3246 15.2 L 13.2536 22.2711 L 9.5109 18.5284 L 8.8038 19.2355 L 13.2536 23.6853 L 13.2536 23.6853 Z"></svg>'; $output = "<button class='elementor-button copy-template ".$condClass."' data-download-ids='{$atts['edd_download_ids']}' data-file-name='{$atts['file_name']}'><span>{$atts['button_text']}</span>{$svgIcon}</button>"; .copy-template { position: relative; margin: 1.5rem 0 1rem; border-radius: 0; padding: 1rem 2rem; display: flex; align-items: center; } .copy-template:hover { background-color: transparent; color: currentColor; } .copy-template:focus { outline: none; background-color: transparent; box-shadow: 0 0 0px 1px #000; color: currentColor; } .copy-template svg { opacity: 1; width: 30px; margin-left: 10px; margin-right: -4px; z-index: 3; transition: opacity 0.3s, transform 0.6s; } .copy-template svg path { fill: #FFF; } .copy-template svg path:nth-child(2) { stroke: transparent; fill: transparent; } .copy-template.fileLoading svg, .copy-template.fileLoaded svg { opacity: 1; transform: translateX(140px) rotate(360deg) scale(4); transform-origin: 50% 60%; } .download-template.download-template.download-template svg{ transform: none; } .copy-template.fileLoading svg path:nth-child(2) { stroke: #000; animation-name: checkItProcessing; animation-duration: 3.7s; animation-iteration-count: 99; animation-fill-mode: forwards; animation-timing-function: ease-out; } .copy-template.fileLoaded svg path:nth-child(2) { stroke: #000; animation-name: checkIt; animation-duration: 1.7s; animation-iteration-count: 1; animation-fill-mode: forwards; animation-timing-function: ease-out; } @keyframes checkItProcessing { 0% { stroke-dashoffset: 20px; stroke-dasharray: 30px; } 100% { stroke-dashoffset: 29px; stroke-dasharray: 10px; } } @keyframes checkIt { 0% { stroke-dashoffset: 20px; stroke-dasharray: 30px; } 100% { stroke-dashoffset: 29px; stroke-dasharray: 0px; } }
List all of the currently logged user meta data key names (not values)
// Add a new menu item to the admin dashboard function add_user_meta_list_menu() { add_menu_page( 'User Meta List', // Page title 'User Meta List', // Menu title 'manage_options', // Capability 'user-meta-list', // Menu slug 'user_meta_list_page', // Function to display the page 'dashicons-admin-users', // Icon 100 // Position ); } add_action('admin_menu', 'add_user_meta_list_menu'); // Function to display the user meta list page function user_meta_list_page() { $user_id = get_current_user_id(); if (empty($user_id)) { echo 'No user is currently logged in.'; return; } $user_meta = get_user_meta($user_id); echo '<div class="wrap">'; echo '<h1>User Meta Keys for User ID: ' . $user_id . '</h1>'; echo '<pre>' . print_r(array_keys($user_meta), true) . '</pre>'; echo '</div>'; }
scrollbar scroll bar styling
.relatedPostsList { max-height: 300px; overflow-y: auto; scrollbar-color: #F4f4f4 #999; scrollbar-width: thin; padding-right: 20px; } /* scroll bar on older Chromium browsers and on Safari */ .relatedPostsList::-webkit-scrollbar-track { background-color: #F4f4f4; } .relatedPostsList::-webkit-scrollbar { width: 10px; background-color: #F4f4f4; } .relatedPostsList::-webkit-scrollbar-thumb { background-color: #999; } /* END OF scroll bar on older Chromium browsers and on Safari */
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; }
Smart email setup for extra email addresses with gmail and no need for any email account
/* from https://www.facebook.com/groups/2147806538801573/ */ I simply forward emails from other accounts to Gmail, and use filters to skip inbox for them and add different labels to them. This eliminates the need for Gmail to retrieve emails from other accounts altogether and hence, there's virtually no delay between the message being received at the address and the message showing up in your Gmail. And also, no limitation on the number of accounts from which you can forward to Gmail. I use the "Send mail as" feature to add those email addresses as sender addresses in Gmail to use when composing/replying. I personally use Amazon SES as the sending service for that, adding and verifying the domains there using DNS records and using their SMTP feature to configure "Send mail as" in Gmail. You can use Brevo, SendGrid or any other third-party sending service for this purpose, as long as it allows verifying your domains for SPF+DKIM and provides SMTP credentials. Usually the free tiers of these services will be more than enough for any typical person's non-bulk email sending requirements in a use case like ours here. Also, when it comes to email *addresses* I need without really needing dedicated email *accounts* on them, I simply set those domains up in Cloudflare and use its email forwarding feature to forward emails on those addresses to Gmail, coupled with Gmail's "Send mail as" feature as described above. This eliminates the need to have any email hosting for those domains in the first place, while giving me the ability to receive emails for those addresses in Gmail and send emails from those addresses from Gmail as well.
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, working with div.progress-swiper-anim
.progress-swiper-anim::before, .progress-swiper-anim::after { content: ''; display: block; position: absolute; width: 30px; height: 30px; top: 16px; right: 16px; border-radius: 50%; border: 3px solid #fff; clip-path: polygon(50% 50%, 0 0, 0 0, 0 0, 0 0, 0 0); } .progress-swiper-anim::after { transform: rotate(180deg); } .swiper-slide-active .progress-swiper-anim::before { animation: prixClipFix 2.3s linear forwards; } .swiper-slide-active .progress-swiper-anim::after { animation: prixClipFix 2.3s 2.3s linear forwards; } @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) } 100% { clip-path: polygon(50% 50%, 0 0, 100% 0, 100% 100%, 100% 100%, 100% 100%) } }
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%)} }
Swiper JS make pagination lines instead of dots, carousel slider
selector .swiper-pagination.swiper-pagination.swiper-pagination > span { margin:0; border-radius:0; width:100px; height:3px; }
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%); }
Fix the Safari iOS White screen space bug after a screen rotation to landscape mode
(function () { let isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; let isSafari = !!navigator.userAgent.match(/Version\/[\d\.]+.*Safari/); let currentScrollPosition = window.scrollY; window.addEventListener("scroll", () => { if (isIOS && isSafari && window.screen.orientation.angle === 0) { currentScrollPosition = window.scrollY; } }); function manageOrientationChange() { let initialScrollY = window.scrollY; /* if smartphone is portrait mode, return early */ if (window.screen.orientation.angle === 0) { return; } document.documentElement.style.height = `initial`; setTimeout(() => { document.documentElement.style.height = `100%`; setTimeout(() => { // this line prevents the content // from hiding behind the address bar window.scrollTo(0, currentScrollPosition + 70); }, 300); }, 300); } if (isIOS && isSafari) { window.addEventListener("orientationchange", manageOrientationChange); } })();
Add gradient edge mask to both ends, for both direction
selector .swiper, selector .swiper-container { --mask-size: 110px; /* gradient edges size desktop */ -webkit-mask: linear-gradient(90deg, transparent, white var(--mask-size), white calc(100% - var(--mask-size)), transparent); } @media (max-width:1024px) { selector .swiper, selector .swiper-container { --mask-size: 40px; /* gradient edges size tablet */ } } @media (max-width:767px) { selector .swiper, selector .swiper-container { --mask-size: 0; /* gradient edges size mobile */ } } .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>