Reinitialize Attribute Functionality in AJAX modal

I am using AJAX to pull content into a modal from a CMS template page instead of navigating to the project template page itself. I’m using the copyclip attribute on the template page so that users can copy the link to the project page easily and share the project with people. It works if I visit the actual template page, but it won’t in the modal because I believe I need to reinitialize the script after the AJAX is finished loading. I know that AJAX doesn’t bring JavaScript and only brings the html over from the target page, so is there some code that can reinitialize or restart the script so that it can be used in the AJAX modal?

Here is the read-only link for the page the AJAX modal lives on. And the template page is the Projects template page if you want to see where I’ve added the copyclip attribute solution.

Webflow - Skycar Creative

Hey @michaellee2245! I see you are adding the script to the page with code, could you try adding these attribute to the script to prevent its loading?

defer fs-attributes-preventload='true'

and initialize it with this line when all the elements are fetched on to the page successfully?

window.fsAttributes.copyclip.init();

Let me know how it goes!

I added the code you recommended, but it didn’t make a difference. So my code now looks like this in the head tag:

<script defer fs-attributes-preventload='true' src='https://cdn.jsdelivr.net/npm/@finsweet/attributes-copyclip@1/copyclip.js'></script>

and in the lightboxReady function I added the other snippet window.fsAttributes.copyclip.init()

Unfortunately, like I mentioned this didn’t fix the problem. When I click the copy button it says the URL is copied but doesn’t actually copy anything. It is still working on the template page though, only not working when the content is being pulled over by the AJAX request.

Hey @michaellee2245! Could you please share a quick video on where to click so that the ajax is fired? It is not clear to me which elements fires this

Sure, here is a loom video that I’m describing how it works and where to find where the AJAX request is being called. Let me know if you have any other questions. Thank you for your help!

https://www.loom.com/share/5f3a7395bc894a5698bb362f3b954629?sid=db2fccf2-49d7-430f-ad1d-539510d04ed6

Hey @michaellee2245! I have tried several methods to initialize the Attribute when all is ready but no luck yet.

One detail that I noticed is that we do have the Attribute below duplicated. This may or may not be the issue.

If this turns out to not be the issue we might need to think about writing our copy to clip solution instead of using the Attributes version

Okay cool, thanks Luis! I’ve removed the second instance of the copy-clip attribute. Now it’s only on the paragraph and not on the div as is correct. I published the page and tried it again but nothing changed. The functionality remained the same of not working in the AJAX modal but working on the actual template page itself.

We must have something blocking the Attribute from working correctly… and I have no idea what could be causing this.

I believe our best bet is to write our own JS to copy the URL from the AJAX modal :confused:

Gotcha… that is frustrating, but I totally get it. Would you be able to help me out with this still? I honestly wouldn’t even know where to start.

Sure! Let me give it a go and I’ll let you know what I come up with!

@michaellee2245! This is what I’ve worked out, Here is the complete function with an extra snippet inside the copyClip function.

<!-- AJAX for loading pages in lightbox modal -->
    <script>
      /*function load_js() {
        var head = document.getElementsByTagName('head')[0];
        var script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/@finsweet/attributes-copyclip@1/copyclip.js';
        head.appendChild(script);
      }*/

      // AJAX MODAL POWER-UP
      window.addEventListener('DOMContentLoaded', (event) => {
        // ajaxmodal component
        function adjaxModal() {
          let lightbox = $("[tr-ajaxmodal-element='lightbox']");
          let lightboxClose = $("[tr-ajaxmodal-element='lightbox-close']").attr(
            'aria-label',
            'Close Modal'
          );
          let lightboxModal = $("[tr-ajaxmodal-element='lightbox-modal']");
          let cmsLink = "[tr-ajaxmodal-element='cms-link']";
          let cmsPageContent = "[tr-ajaxmodal-element='cms-page-content']";
          let initialPageTitle = document.title;
          let initialPageUrl = window.location.href;
          let focusedLink;

          function copyClip() {
            document.querySelectorAll('[fs-copyclip-element="click"]').forEach(function (element) {
              element.addEventListener('click', function () {
                let targetElement = document.querySelector('[fs-copyclip-element="copy-sibling"]');
                let textToCopy = targetElement.firstChild.textContent;
                navigator.clipboard.writeText(textToCopy).then(
                  function () {
                    console.log('Text copied to clipboard');
                  },
                  function (err) {
                    console.error('Failed to copy text: ', err);
                  }
                );
              });
            });
          }

          function updatePageInfo(newTitle, newUrl) {
            lightboxModal.empty();
            document.title = newTitle;
            window.history.replaceState({}, '', newUrl);
          }

          let tl = gsap.timeline({
            paused: true,
            onReverseComplete: () => {
              focusedLink.focus();
              updatePageInfo(initialPageTitle, initialPageUrl);
            },
            onComplete: () => {
              lightboxClose.focus();
            },
          });
          tl.set('.navigation', { zIndex: 1 });
          tl.set('body', { overflow: 'hidden' });
          tl.set(lightbox, { display: 'block', onComplete: () => lightboxModal.scrollTop(0) });
          tl.from(lightbox, { opacity: 0, duration: 0.2 });
          tl.from(lightboxModal, { y: '5em', duration: 0.2 }, '<');

          function keepFocusWithinLightbox() {
            let lastFocusableChild = lightbox
              .find("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])")
              .not(':disabled')
              .not('[aria-hidden=true]')
              .last();
            lastFocusableChild.on('focusout', function () {
              lightboxClose.focus();
            });
          }

          function lightboxReady() {
            //load_js();
            copyClip(); // Function to copy the page's url to clipboard
            let mm = gsap.matchMedia();

            mm.add('(min-width: 991px)', () => {
              let share = gsap.timeline({ paused: true });

              share
                .to('.gallery_copy-link-btn', {
                  width: '100%',
                  ease: 'power2.out',
                  duration: 0.3,
                })
                .to(
                  '.gallery_copy-text',
                  {
                    display: 'block',
                    opacity: 1,
                    duration: 0.1,
                    ease: 'power2.inOut',
                    delay: 0.25,
                  },
                  0
                );
              $('.gallery_copy-btn').on('mouseenter', function () {
                share.play();
              });
              $('.gallery_copy-btn').on('mouseleave', function () {
                share.reverse();
              });
            });
          }

          $(document).on('click', cmsLink, function (e) {
            focusedLink = $(this);
            initialPageUrl = window.location.href;
            e.preventDefault();
            let linkUrl = $(this).attr('href');
            $.ajax({
              url: linkUrl,
              success: function (response) {
                let cmsContent = $(response).find(cmsPageContent);
                let cmsTitle = $(response).filter('title').text();
                let cmsUrl = window.location.origin + linkUrl;
                updatePageInfo(cmsTitle, cmsUrl);
                lightboxModal.append(cmsContent);
                tl.play();
                keepFocusWithinLightbox();
                lightboxReady();
              },
            });
          });

          lightboxClose.on('click', function () {
            tl.reverse();
          });
          $(document).on('keydown', function (e) {
            if (e.key === 'Escape') tl.reverse();
          });
          $(document).on('click', lightbox, function (e) {
            if (!$(e.target).is(lightbox.find('*'))) tl.reverse();
          });
        }
        adjaxModal();
      });
    </script>

This should work as is with your setup and should not interfere with the template page setup.

Let me know if this is what you were looking for!

1 Like

This works! It’s copying the URL perfectly, you’re amazing! The only thing that is off is that the text doesn’t update to say “Copied” after the Copy button is clicked. This is a fairly minor thing, but do you have an idea why that piece isn’t working?

Apologies! Small oversight from my end.

Can you please test this corrected function?

  function copyClip() {
    document.querySelectorAll('[fs-copyclip-element="click"]').forEach(function (element) {
      element.addEventListener('click', function () {
        let targetElement = document.querySelector('[fs-copyclip-element="copy-sibling"]');
        let textToCopy = targetElement.firstChild.textContent;
        let messageElement = element.firstChild;

        navigator.clipboard.writeText(textToCopy).then(
          function () {
            let message = element.getAttribute('fs-copyclip-message');
            let duration = parseInt(element.getAttribute('fs-copyclip-duration'));
            let copyCTA = messageElement.innerText;

            messageElement.innerText = message;

            setTimeout(function () {
              messageElement.innerText = copyCTA;
            }, duration);
          },
          function (err) {
            console.error('Failed to copy text: ', err);
          }
        );
      });
    });
  }

Let me know how it goes!

1 Like

Finally implemented this and it worked perfectly! Thank you for all your help troubleshooting this problem and helping to provide a working solution! :tada:

1 Like

Always happy to help!