Search code examples
replaceprototypejsjquery

Prototype.js stops working after adding jQuery .replace(), other jQuery works just fine


I am using Acuity Scheduling for a project. It uses prototype.js and allows me to add my own custom code to the head and footer of the page (served to my site via iframe). I'm not familiar with prototype.js, so I'm using jQuery in a way that it won't conflict. My jQuery code and prototype.js were working just fine, until I added this code:

jQuery('body').html(jQuery('body').html().replace('an Appointment','a Session'));

I'm looking for a way to replace specific words in the iframe using jQuery without breaking the other jQuery code or prototype.js.

You can see the content of my iframe here: https://acuityscheduling.com/schedule.php?owner=11134756

If you view the source, you'll see the code I added at the bottom:

<script language="javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script> 

<script language="javascript">
  jQuery.noConflict();

  jQuery(document).ready(function(){
    jQuery('body').on('click', '.time-selection', function() {
      jQuery('.continueAlert').hide();   
      jQuery('#rest').append('<p class="continueAlert">Please enter your name and contact info below.</p>');  
    });

    jQuery('body').html(jQuery('body').html().replace('an Appointment','a Session'));

  });
</script>

Thanks for any help you can offer!


Solution

  • I see you have some event listeners setup by prototype targeting specific elements:

    Event.observe(input, 'focus', function(){InputPrompt.focus(input);});
    Event.observe(input, 'blur', function(){InputPrompt.blur(input);});
    Event.observe(input, 'keypress', function(){input.label.hide();});
    

    (there may be more of them, but these were what I was able to quickly spot)

    When you replace an element's innerHTML property (which is what you're doing with your jQuery find/replace snippet), the browser basically "throws away" the old DOM elements and creates new ones. So the elements you're seeing on the page after you update innerHTML aren't the same once you were previously seeing, which were the ones that have the event listeners attached to them. That's why everything "stopped working"

    I see two options:

    1. Update your find/replace script to only update text nodes. That will ensure that the containing elements that have the event listeners won't get messed with.

    2. Use event delegation that doesn't target specific elements. Check out Event.on, paying close attention to the optional 'selector' parameter. Something like:

      document.on('focus', 'input', function(event, inputElement) { InputPrompt.focus(inputElement); });

    I feel like the the first option would be less intrusive to the code that's already established on this page.

    Edit: Here's a very brute-force method to find/replace on all text nodes (using Prototype). There's probably a more efficient way of doing this. Unfortunately, you can't use a CSS selector to match on text nodes, hence all the filtering of childNodes and whatnot -

    document.body.select('*:not(script)').each(function(el){
      $A(el.childNodes).each(function(child){
        if (child.nodeType === 3) { // only get text nodes
          child.nodeValue = child.nodeValue.replace('an Appointment', 'a Session');
        }
      })
    });