⚙️ Webflow Script Builder

This is a collection of scripts that we have hand-crafted specifically for extending the capabilities of Webflow beyond what is currently offered. These should always be tested on staging before pushing live.

⚠️ Remember when you paste these scripts into your footer code, you'll want to make sure to paste them within the document ready function.
<script>
  $("document").ready(function() {
    // Code goes here
  });
</script>
Copy Code

📝 Form Enhancements

Append current page URL to all forms

This script appends the current page URL as a hidden text input to all forms on the website. It allows you to capture the URL from which the form was submitted.
jQuery
// Append current page URL to all forms as a hidden text input
$('form').append(`<input type="hidden" id="insertpageurl" name="Page URL" value="${location.href}" />`);
Copy Code

Append user's IP address to all forms

This script retrieves the user's IP address using an API and appends it as a hidden text input to all forms on the website. It helps in capturing the user's IP address along with form submissions.
jQuery
// Append user's IP address to all forms
$.getJSON("https://api.ipify.org?format=json", function(data) {
  $('form').append(`<input type="hidden" id="insertipaddress" name="IP Address" value="${data.ip}" />`);
});
Copy Code

Prevent empty dropdown form field options from being selected

This script disables all form dropdown options with no value set.
jQuery
// Disable empty dropdown form field option
$('form option[value=""]').attr('disabled', true);
Copy Code

Format phone number input to U.S. format

This script formats the phone number input field to the U.S. format when the user unfocuses the field. It ensures that phone numbers entered by users are displayed consistently.

⚠️ You must add a custom attribute to the field named "format-phone" and leave the value empty.
jQuery
$('input[format-phone]').focusout(function() {
  var phone = $(this).val();
  function formatPhoneNumber(phoneNumberString) {
    var cleaned = ('' + phoneNumberString).replace(/\D/g, '');
    var match = cleaned.match(/^(\d{3})(\d{3})(\d{4})$/);
    if (match) {
      return '(' + match[1] + ') ' + match[2] + '-' + match[3];
    }
    return null;
  }
  $(this).val(formatPhoneNumber(phone));
});
Copy Code

🎉 Fun Stuff

Keep copyright year up to date

This script updates the copyright year dynamically to the current year. It ensures that the year displayed in the copyright notice is always up to date.

⚠️ You must add a custom attribute to an element named "update-year" and leave the value empty. This will replace the text inside your element with the current year.
jQuery
changeYear = () => {
  var newDate = new Date();
  var dateYear = newDate.getFullYear();
  $('[update-year]').html(dateYear);
}
Copy Code

Count Up Numbers

This script defines the easeInOutQuad easing function and then applies a count-up animation to elements with the attribute [countup]. Each element's text value will be gradually animated from 0 to the specified countTo value using the defined easing function. This animation will trigger when the element is scrolled into view

⚠️ You must add a custom attribute to an element named "countup" and the value should be the desired value to be counted up to.
jQuery

// Define the easeInOutQuad easing function
$.easing.easeInOutQuad = function(x, t, b, c, d) {
  if ((t /= d / 2) < 1) return c / 2 * t * t + b;
  return -c / 2 * ((--t) * (t - 2) - 1) + b;
};

// Listen to the scroll event on the window
$(window).on('scroll', function() {
  // Iterate through all elements with the 'countup' attribute
  $('[countup]').each(function() {
    var $this = $(this);
    var countTo = parseInt($this.attr('countup'));
    var windowHeight = $(window).height();
    var scrollPosition = $(window).scrollTop();
    var elementPosition = $this.offset().top;

    // Check if the countup animation has not been triggered and the element is within the viewport
    if (!($this.data('countup-triggered')) && elementPosition < (scrollPosition + windowHeight)) {
      $this.data('countup-triggered', true);

      // Animate the countup
      $({ countNum: 0 }).animate({
        countNum: countTo
      }, {
        duration: 2500,
        easing: 'easeInOutQuad',
        step: function() {
          $this.text(Math.floor(this.countNum));
        },
        complete: function() {
          $this.text(this.countNum);
        }
      });
    }
  });
});
Copy Code

Confetti

This script enables a confetti effect on an element when it has the attribute confetti-target set with a class of ".active". It uses CSS animations and dynamically adds confetti particles to create a festive visual effect

⚠️ [confetti-target] is used to identify the element that will display the confetti effect, while [confetti-trigger] is used to identify the element that triggers the activation or deactivation of the confetti effect. When [confetti-target] is given the ".active" class, the confetti will begin.

Here's an example of toggling the ".active" class on a [confetti-target] element.
Confetti Trigger

Confetti Target

CSS
[confetti-target].active {
    position: relative;
}

[confetti-target].active > .particle {
    opacity: 0;
    position: absolute;
    animation: confetti 3s ease-in infinite;
    z-index: 0;
}
[confetti-target].active > .particle.c1 {
    background-color: #f47929;
}
[confetti-target].active > .particle.c2 {
    background-color: #f47929;
}

@keyframes [confetti-target] {
    0% {
        opacity: 0;
        transform: translateY(0%) rotate(0deg);
    }
    10% {
        opacity: 1;
    }
    35% {
        transform: translateY(-800%) rotate(270deg);
    }
    80% {
        opacity: 1;
    }
    100% {
        opacity: 0;
        transform: translateY(2000%) rotate(1440deg);
    }
}
Copy Code
jQuery

function initparticles() {
    confetti();
}

function confetti() {
    $.each($("[confetti-target].active"), function(){
        var confetticount = ($(this).width()/50)*5;
        for(var i = 0; i <= confetticount; i++) {
            $(this).append('<span class="particle c' + $.rnd(1,2) + '" style="top:' + $.rnd(10,50) + '%; left:' + $.rnd(0,100) + '%;width:' + $.rnd(6,8) + 'px; height:' + $.rnd(3,4) + 'px;animation-delay: ' + ($.rnd(0,30)/10) + 's;"></span>');
        }
    });
}

jQuery.rnd = function(m,n) {
    m = parseInt(m);
    n = parseInt(n);
    return Math.floor( Math.random() * (n - m + 1) ) + m;
}

initparticles();

$("[confetti-trigger]").on("click", function() {
   $(`[confetti-target=${$(this).attr('confetti-trigger')}]`).toggleClass("active");
   initparticles();
});
Copy Code

🎯 Sticky CTA Bar

Fixed Footer CTA Display logic

This script controls the display logic of a fixed footer call-to-action (CTA) element. It shows the fixed CTA when the user scrolls up and hides it when the user scrolls down.
jQuery
var lastScrollTop = 0;
var fixedNav = $('.fixed-footer-cta');
$(window).scroll(function() {
  var st = $(this).scrollTop();
  ((st < lastScrollTop)) ? fixedNav.slideDown(150) : fixedNav.slideUp(150);
  lastScrollTop = st;
});
Copy CodeCopy Webflow Component
{"type":"@webflow/XscpData","payload":{"nodes":[{"_id":"f5032ea6-3af0-b220-b5a1-66b1db3e0a24","type":"Block","tag":"div","classes":["91b1675b-f2f6-6b38-ecb9-b566d0ab8103"],"children":["e549b15b-dc65-567b-60f5-474fb670aa63"],"data":{"search":{"exclude":false},"xattr":[],"text":false,"displayName":"","devlink":{"runtimeProps":{},"slot":""},"attr":{"id":""},"visibility":{"conditions":[]},"tag":"div"}},{"_id":"e549b15b-dc65-567b-60f5-474fb670aa63","type":"Block","tag":"div","classes":["989b8211-9a25-2dd9-6dc5-d95b60618e3b"],"children":["2ddea78a-fe47-fb9a-2a9c-636c9c1e607d","0e91b977-4b0a-4ab8-cde8-c7fefdada272"],"data":{"search":{"exclude":false},"xattr":[],"text":false,"displayName":"","devlink":{"runtimeProps":{},"slot":""},"attr":{"id":""},"visibility":{"conditions":[]},"tag":"div"}},{"_id":"2ddea78a-fe47-fb9a-2a9c-636c9c1e607d","type":"Block","tag":"div","classes":["319311ed-2992-ebee-1dc8-aeebe4767702"],"children":["078a18bb-2091-1de3-4d97-8a7a2541b988"],"data":{"search":{"exclude":false},"xattr":[],"text":false,"displayName":"","devlink":{"runtimeProps":{},"slot":""},"attr":{"id":""},"visibility":{"conditions":[]},"tag":"div"}},{"_id":"078a18bb-2091-1de3-4d97-8a7a2541b988","type":"Block","tag":"div","classes":[],"children":["078a18bb-2091-1de3-4d97-8a7a2541b989"],"data":{"search":{"exclude":false},"xattr":[],"text":true,"displayName":"","devlink":{"runtimeProps":{},"slot":""},"attr":{"id":""},"visibility":{"conditions":[]},"tag":"div"}},{"_id":"078a18bb-2091-1de3-4d97-8a7a2541b989","text":true,"v":"Contact us to learn how our water systems are better than the rest!"},{"_id":"0e91b977-4b0a-4ab8-cde8-c7fefdada272","type":"Block","tag":"div","classes":["7d2e8abb-ae07-e6d7-ae9a-fe88cc9674c1"],"children":["ea06b163-1e10-5007-0bd1-f7b6944f313d","812ff0d9-4806-ad32-1117-126a281098dd"],"data":{"search":{"exclude":false},"xattr":[],"text":false,"displayName":"","devlink":{"runtimeProps":{},"slot":""},"attr":{"id":""},"visibility":{"conditions":[]},"tag":"div"}},{"_id":"ea06b163-1e10-5007-0bd1-f7b6944f313d","type":"Link","tag":"a","classes":["29f08a7e-59e0-9f0e-abfa-0e1a78b75cf4","f1f16c0d-1e29-db5b-511b-d04786a00088"],"children":["733450b5-1dd0-7caf-80e7-acf8b108014f"],"data":{"search":{"exclude":false},"xattr":[],"block":"inline","displayName":"","devlink":{"runtimeProps":{},"slot":""},"attr":{"id":"","href":"#"},"visibility":{"conditions":[]},"button":false,"link":{"mode":"external"}}},{"_id":"733450b5-1dd0-7caf-80e7-acf8b108014f","type":"Block","tag":"div","classes":[],"children":["733450b5-1dd0-7caf-80e7-acf8b1080150"],"data":{"search":{"exclude":false},"xattr":[],"text":true,"displayName":"","devlink":{"runtimeProps":{},"slot":""},"attr":{"id":""},"visibility":{"conditions":[]},"tag":"div"}},{"_id":"733450b5-1dd0-7caf-80e7-acf8b1080150","text":true,"v":"Contact Us"},{"_id":"812ff0d9-4806-ad32-1117-126a281098dd","type":"Link","tag":"a","classes":["29f08a7e-59e0-9f0e-abfa-0e1a78b75cf4","f1f16c0d-1e29-db5b-511b-d04786a00088","f84765fc-b784-85ef-eab2-2f07603fdd18"],"children":["812ff0d9-4806-ad32-1117-126a281098de"],"data":{"search":{"exclude":false},"xattr":[],"block":"inline","displayName":"","devlink":{"runtimeProps":{},"slot":""},"attr":{"id":""},"visibility":{"conditions":[]},"button":false},"bind":{"type":"Record","val":{"link":{"type":"Select","val":{"from":{"type":"Variable","val":"data"},"prop":"a8392d8d-9208-66cd-bed1-f41ccfe736a8"}}}}},{"_id":"812ff0d9-4806-ad32-1117-126a281098de","type":"Block","tag":"div","classes":[],"children":[],"data":{"text":true,"tag":"div","displayName":"","attr":{"id":""},"xattr":[],"search":{"exclude":false},"visibility":{"conditions":[]},"devlink":{"runtimeProps":{},"slot":""}},"bind":{"type":"Record","val":{"children":{"type":"Select","val":{"from":{"type":"Variable","val":"data"},"prop":"30b3dc42-769c-2efa-893f-e11cd5ca65d5"}}}}}],"styles":[{"_id":"91b1675b-f2f6-6b38-ecb9-b566d0ab8103","fake":false,"type":"class","name":"fixed-footer-cta","namespace":"","comb":"","styleLess":"position: fixed; left: 0px; bottom: 0px; z-index: 5; display: none; width: 100%; max-width: 100vw; padding-bottom: 0px; justify-content: space-between; align-items: center; border-top-style: solid; border-top-width: 1px; border-top-color: hsla(0, 0.00%, 0.00%, 0.10); background-color: whitesmoke; line-height: 1;","variants":{"small":{"styleLess":"border-top-style: none; background-color: transparent;"}},"children":[],"createdBy":"5e456b522ed599ad7ad8608c","selector":null},{"_id":"989b8211-9a25-2dd9-6dc5-d95b60618e3b","fake":false,"type":"class","name":"flex-apart-center","namespace":"","comb":"","styleLess":"display: flex; justify-content: space-between; align-items: center;","variants":{},"children":[],"createdBy":"5e456b522ed599ad7ad8608c","selector":null},{"_id":"319311ed-2992-ebee-1dc8-aeebe4767702","fake":false,"type":"class","name":"fixed-footer-cta-text","namespace":"","comb":"","styleLess":"padding-top: 1.1rem; padding-right: 1.1rem; padding-bottom: 1.1rem; padding-left: 1.1rem; font-weight: 700;","variants":{"small":{"styleLess":"display: none;"}},"children":[],"createdBy":"5e456b522ed599ad7ad8608c","selector":null},{"_id":"7d2e8abb-ae07-e6d7-ae9a-fe88cc9674c1","fake":false,"type":"class","name":"fixed-footer-cta-buttons","namespace":"","comb":"","styleLess":"display: flex; justify-content: flex-end;","variants":{"medium":{"styleLess":"width: 60%;"},"small":{"styleLess":"width: 100%;"}},"children":[],"createdBy":"5e456b522ed599ad7ad8608c","selector":null},{"_id":"29f08a7e-59e0-9f0e-abfa-0e1a78b75cf4","fake":false,"type":"class","name":"btn","namespace":"","comb":"","styleLess":"margin-top: 1rem; margin-right: 1rem; margin-left: auto; padding-top: 0.75em; padding-right: 1.5em; padding-bottom: 0.75em; padding-left: 1.5em; border-top-style: none; border-top-width: 1px; border-top-color: hsla(0, 0.00%, 0.00%, 0.00); border-right-style: none; border-right-width: 1px; border-right-color: hsla(0, 0.00%, 0.00%, 0.00); border-bottom-style: solid; border-bottom-width: 0.25rem; border-bottom-color: hsla(0, 0.00%, 0.00%, 0.10); border-left-style: none; border-left-width: 1px; border-left-color: hsla(0, 0.00%, 0.00%, 0.00); border-top-left-radius: 0.25rem; border-top-right-radius: 0.25rem; border-bottom-left-radius: 0.25rem; border-bottom-right-radius: 0.25rem; background-color: hsla(51, 100.00%, 45.00%, 1.00); transition-property: all; transition-duration: 300ms; transition-timing-function: ease-in-out; color: hsla(37.633136094674555, 0.00%, 100.00%, 1.00); line-height: 1; font-weight: 900; text-align: center; letter-spacing: 0.75px; text-decoration: none; text-transform: uppercase; white-space: normal;","variants":{"main_hover":{"styleLess":"background-color: hsla(51, 98.23%, 44.06%, 1.00); transform: translate(0px, -2px); color: hsla(37.633136094674555, 0.00%, 100.00%, 1.00);"}},"children":["29f08a7e-59e0-9f0e-abfa-0e1a78b75cf5","a43d9803-e949-fc82-512a-37c0833ab6d6","9b31c3cf-3344-33ed-2141-29f16842e011","20b4e55a-7004-3294-fc9a-82a9ef748b8c","823416bc-a671-b998-4d45-5fef28de3053","35098cc0-1fb8-3f49-95cc-a59c1cd63da2","f1f16c0d-1e29-db5b-511b-d04786a00088","b625920f-98f5-29ab-e3ae-900573856db4"],"selector":null},{"_id":"f1f16c0d-1e29-db5b-511b-d04786a00088","fake":false,"type":"class","name":"fixed-footer-cta-btn","namespace":"","comb":"&","styleLess":"height: 100%; margin-top: 0.5rem; margin-bottom: 0.5rem; margin-left: 0px;","variants":{"small":{"styleLess":"width: 50%; margin-top: 0rem; margin-right: 0rem; margin-bottom: 0rem;"},"tiny":{"styleLess":"display: flex; height: auto; justify-content: center; align-items: center;"}},"children":["f84765fc-b784-85ef-eab2-2f07603fdd18"],"createdBy":"5e456b522ed599ad7ad8608c","selector":null},{"_id":"f84765fc-b784-85ef-eab2-2f07603fdd18","fake":false,"type":"class","name":"btn-2","namespace":"","comb":"&","styleLess":"","variants":{},"children":[],"createdBy":"5e456b522ed599ad7ad8608c","selector":null}],"assets":[],"ix1":[],"ix2":{"interactions":[],"events":[],"actionLists":[]}},"meta":{"unlinkedSymbolCount":0,"droppedLinks":1,"dynBindRemovedCount":0,"dynListBindRemovedCount":0,"paginationRemovedCount":0}}

🤖 SEO Scripts

Canonicalize a paginated blog

This script adds canonical links to paginated CMS pages. It retrieves the previous and next page URLs from the pagination links and appends the appropriate tags to the website's head. Additionally, if the current page has a query parameter _page, indicating a paginated blog page, it adds a canonical link to the main CMS page.

⚠️ You must add this to the page-level footer code, no need to run this globally.
jQuery
var cms_prev_link = window.location.origin + window.location.pathname + $('.w-pagination-previous').attr('href');
var cms_next_link = window.location.origin + window.location.pathname + $('.w-pagination-next').attr('href');

if ($('.w-pagination-previous').attr('href')) {
  prev_canonical = "<link rel='prev' href="" + cms_prev_link.toString() +"" />";
  $('head').append(prev_canonical);
}

if ($('.w-pagination-next').attr('href')) {
  next_canonical = "<link rel='next' href="" + cms_next_link.toString() + "" />";
  $('head').append(next_canonical);
}

if (window.location.search.indexOf('_page=') > 0) {
  $('head').append("<link rel="canonical" href="" + window.location.origin + "/blog">");
}
Copy Code

Store form values as first-party cookies for GTM

An event listener is added to form elements with the "gtm" attribute. Whenever a change event occurs, the script captures the attribute value ("gtm") and the corresponding input value. It then sets a cookie with the "gtm" attribute value as the name and the input value as the value.

⚠️ You must add a custom attribute named "gtm" to each input you want stored. The value should exactly match the name of your cookie in GTM (case-sensitive).
jQuery
// Get the base website URL
var baseUrl = document.location.hostname;

// Event listener for changes in form elements with "gtm" attribute
$("form [gtm]").on("change", function() {
  var input = $(this);
  var name = input.attr("gtm"); // Get the "gtm" attribute value for naming the cookie
  var value = input.val();

  if (input.attr("type") === "checkbox") {
    // Get all checked checkboxes with the same "gtm" attribute value
    var checkboxes = $("form [gtm='" + name + "']:checked");
    // Map the values of the checked checkboxes
    var values = checkboxes.map(function() {
      return this.value;
    }).get().join(",");
    // Set the cookie with the "gtm" attribute value as the name and the mapped values as the value
    document.cookie = name + "=" + values + "; path=/; domain=" + baseUrl;
  } else if (input.attr("type") === "radio") {
    // Get all radio inputs with the same "gtm" attribute value
    var radioButtons = $("form [gtm='" + name + "']");
    radioButtons.each(function() {
      if ($(this).prop("checked")) {
        // Set the cookie with the "gtm" attribute value as the name and the value of the selected radio as the value
        document.cookie = name + "=" + $(this).val() + "; path=/; domain=" + baseUrl;
      }
    });
  } else {
    // For other inputs, set the cookie with the "gtm" attribute value as the name and the input value as the value
    document.cookie = name + "=" + value + "; path=/; domain=" + baseUrl;
  }
});
Copy Code