4

I have some custom JavaScript on my SquareSpace site that manipulates Product titles beyond what you can do with SquareSpace's default style editor. It works when initially loading the page (https://www.manilva.co/catalogue-accessories/) but if you click on any of the categories on the left, the styling resets to the default.

I'm assuming the JavaScript is being overwritten by the SquareSpace style, but I can't figure out why. Perhaps I'm calling the function in the wrong place?

Any suggestions would be helpful.

Thanks!

Current code:

document.querySelectorAll(".ProductList-filter-list-item-link".forEach(i=>i.addEventListener("click", function()
  {
      var prodList = document.querySelectorAll("h1.ProductList-title");
  for (i = 0, len = prodList.length; i < len; i++) 
  {
    var text = prodList[i].innerText;
    var index = text.indexOf('-');
    var lower = text.substring(0, index);
    var higher = text.substring(index + 2);
    prodList[i].innerHTML = lower.bold() + "<br>" + higher;

  });
Peter Lawson
  • 67
  • 1
  • 5

2 Answers2

6

The source of your problem is that your template has AJAX loading enabled. There are currently a couple generally-accepted ways to deal with this as a Squarespace developer:

  1. Disable AJAX loading
  2. Write your javascript functions in a manner that will run on initial site load and whenever an "AJAX load" takes place.

Option 1 - Disable AJAX:

  1. In the Home Menu, click Design, and then click Site Styles.
  2. Scroll down to Site: Loading.
  3. Uncheck Enable Ajax Loading.


Option 2 - Account for AJAX in Your JS

There are a number of ways that developers approach this, including the following, added via sitewide code injection:

<script>
window.Squarespace.onInitialize(Y, function() {
  // do stuff
});
</script>

or

<script>
(function() {
  // Establish a function that does stuff.
  var myFunction = function() {
    // Do stuff here.
  };

  // Initialize the fn on site load.
  myFunction();
  // myFunction2(); , etc...

  // Reinit. the fn on each new AJAX-loaded page.
  window.addEventListener("mercury:load", myFunction);
})();
</script>

or

<script>
(function() {
  // Establish a function that does stuff.
  var myFunction = function() {
    // Do stuff here.
  };

  // Initialize the fn on site load.
  myFunction();

  // Reinit. the fn on each new AJAX-loaded page.
  new MutationObserver(function() {
    myFunction();
    // myFunction2(); , etc...
  }).observe(document.body, {attributes:true, attributeFilter:["id"]});
})();
</script>

Each of those works for most of the latest (at time of writing) templates most of the time. Each of those have their advantages and disadvantages, and contexts where they do not work as one might expect (for example, on the /cart/ page or other "system" pages). By adding your code within the context of one of the methods above, and ensuring that the code is of course working in the desired contexts and without its own bugs/issues, you will have your code run on initial site load and on each AJAX page load (with some exceptions, depending on the method you use).

Brandon
  • 3,572
  • 2
  • 12
  • 27
  • Thanks Brandon! That's perfect and works! I've disabled AJAX loading for now as I'm still very new to Squarespace development but in the future I'll look to add the code and keep AJAX loading enabled. – Peter Lawson Feb 02 '19 at 13:19
  • Regarding option 2, if I'm adding code to listen for the AJAX load, that code needs to be added when the page first loads, right? So I'd have to add it to every page header of the site (using a loaded script). And then, if I wanted code that was only specific to one page, I'd have to detect which page is being loaded and check if it's the right page? – Andrew Nov 20 '19 at 15:18
  • 1
    @Andrew: If I'm understanding, then the answers are 1) all of the above snippets in option two account for both the initial load and subsequent AJAX loads. The first snippet uses `.onInitialize` which runs in both cases and the second two snippets use an initial function call for the first page load then an eventListener or mutationObserver for AJAX loads. 2) You can add it via sitewide footer or header code injection, which avoids adding to every page (see edited answer). 3) Yes, `// Do stuff here` needs to include logic that checks for the proper/applicable conditions to run your other code. – Brandon Nov 20 '19 at 16:00
  • Yes that basically answers my questions, thank you! It still wouldn't solve the case, for instance, of allowing you to use custom "code" blocks within SquareSpace that have JavaScript (say, if you're writing a blog post with some isolated code). I see that the – Andrew Nov 20 '19 at 16:07
  • I may not be understanding, but if you're adding a code block to the body of a specific post, then that code will only run when that block post is loaded which, on the surface, seems to remove the need for this solution entirely. Furthermore, in most cases I would avoid adding JavaScript via code blocks, preferring instead to, say, insert some markup (`
    `) in the code block and maintain JS code within global injection points, detecting the presence of targetDiv from there. Of course, you may well be referring to an entirely valid scenario where that would not work.
    – Brandon Nov 20 '19 at 16:15
  • 1
    If you add an HTML block on a Squarespace page, and put a – Andrew Nov 20 '19 at 16:39
  • Now I get you. Based on your comments, I started to suspect that it might be the case that ` – Brandon Nov 20 '19 at 16:49
  • I wrote a potential solution in this question, but I'm not sure if it's a security risk so won't post it here yet: https://stackoverflow.com/questions/58960105/is-it-safe-to-re-execute-an-inserted-script-node – Andrew Nov 21 '19 at 01:16
1

Your problem is the page does not reload when clicking a button on the left, just some elements are removed, added and replaced. The changed elements will not be restyled. You will need to re-run your JavaScript after one of those buttons is clicked. Perhaps something like this:

document.querySelectorAll(
    ".ProductList-filter-list-item"
).forEach(
    i=>i.addEventListener(
        "click", ()=>console.log("hello")
    )
)

where you replace console.log("hello") with whatever resets your formatting.

mousetail
  • 7,009
  • 4
  • 25
  • 45
  • Thanks - will give this a shot! Just so I'm clear, where you say replace console.log, you mean with the function I've made to reset the formatting? – Peter Lawson Feb 02 '19 at 11:04
  • Yes. You will also need to add the event handles again every time. – mousetail Feb 02 '19 at 11:06
  • I've updated my question with the code I've added to try and achieve this, though it's not currently working and I'm receiving an error. – Peter Lawson Feb 02 '19 at 11:12
  • Seems like you are missing the ")" for querySelector – mousetail Feb 02 '19 at 11:57
  • I've fixed all the errors, it appears that when I click a button it's applying the code to the page before the button is clicked, rather than to the titles after the click. – Peter Lawson Feb 02 '19 at 12:17