Answers.Body: <p>This is oddly similar to <a href="https://craftcms.stackexchange.com/questions/38042/swiper-breaks-when-using-sprig">this question</a>.</p>
  <p>The problem is that your Sprig component includes the wrapper (the <code>div</code> with the <code>masonry</code> class), so reloading the Sprig component not only replaces the items, but also the wrapper itself. Masonry keeps a reference to this wrapper (the element masonry was initialized with) and uses it to find new children. Since the original wrapper was removed from the DOM, masonry can't find the new items.</p>
  <p>You can verify that by running <code>window.masonryLayout.getItemElements()</code> in your component script. It will always include the original six items, regardless of the current filters.</p>
  <p>The easiest solution would be to completely remove the Masonry instance using <a href="https://masonry.desandro.com/methods.html#destroy" rel="nofollow noreferrer">.destroy()</a> and initialize a new instance with the new wrapper whenever the component is reloaded. Not sure if that can be done with your current setup, since the Sprig component won't have access to the Masonry class. You could probably integrate the reinitialization logic in your compiled JS code by watching for DOM changes using a <a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver" rel="nofollow noreferrer">Mutation Observer</a>. Or just expose the Masonry class to the window object in your Webpack build step, this way you can initialize a new instance in your Sprig component script.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-08-13T20:06:16.533Z
Answers.Text: Thank you!! Tried using MutationObserver, but it wasn't detecting child node changes, though I think I could have gotten that sorted with some more time. For now, added the Masonry class to the window object and everything is working . Added working code to my answer.
Answers.UserDisplayName: Bryce
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-08-13T07:39:21.757Z
Answers.LastActivityDate: 2021-08-13T07:39:21.757Z
Answers.UserDisplayName: MoritzLost
Body: <p>I've got Sprig set up to filter entries by a few categories. I'm trying to re-layout Masonry JS after this update takes place.</p>
  <p>For starters, I add Masonry to the window variable (defined as global in my JS module since I'm using node):</p>
  <p><code>global.masonryLayout = new Masonry('.masonry', options)</code></p>
  <p>Then, in my sprig component, I'm using a <code>&lt;script&gt;</code> tag that runs each time sprig loads entries, as per the docs:</p>
  <pre><code>&lt;script&gt;
    if (window.masonryLayout) {
      window.masonryLayout.reloadItems() 
    }
  &lt;/script&gt;
  </code></pre>
  <p>I've got an example up and running, including console outputs each time the script runs. The Masonry class is there, but it just isn't laying out.</p>
  <ol>
  <li><a href="https://masonry.brycekirk.com/press" rel="nofollow noreferrer">https://masonry.brycekirk.com/press</a></li>
  <li>Filter by Source &gt; The New York Times</li>
  </ol>
  <p><b>Expected behaviour:</b> Items are loaded into a Masonry layout.</p>
  <p><b>Actual behaviour:</b> No errors, but the layout is just normal columns, not Masonry (the 2nd row of articles are on their own line below the others).</p>
  <p>Right now I'm using <code>.reloadItems()</code>, which actually populates <code>colYs</code> and <code>cols</code>.</p>
  <p><code>.layout()</code> leaves <code>colYs</code> and <code>cols</code> blank.</p>
  <p>What am I missing here??</p>
  <p><b>EDIT: Working code based on MoritzLost's answer</b></p>
  <p>In my JS module:</p>
  <pre><code>global.Masonry = Masonry
  global.masonryOptions = options
  global.masonryLayout = new Masonry('.masonry', options)
  </code></pre>
  <p>In my sprig component:</p>
  <pre><code>if (window.masonryLayout) {
      window.masonryLayout.destroy()
      window.masonryLayout = new window.Masonry('.masonry', window.masonryOptions)
  }
  </code></pre>
  <p><strong>EDIT (JULY 2023):</strong>
  This is much easier with htmx events. Just need to make sure to load the sprig script <em>before</em> your sprig component.</p>
  <p>JS module:</p>
  <pre><code>global.Masonry = Masonry
  global.masonryOptions = options // These are just masonry options I've defined in an object variable
  
  window.onload = () =&gt; {
    global.masonryLayout = new Masonry('.masonry', options)
  }
  </code></pre>
  <p>In my sprig component:</p>
  <pre><code>  htmx.on('htmx:beforeSwap', function(e) {
      window.masonryLayout.destroy()
    })
    
    htmx.on('htmx:afterSwap', function(e) {
      window.masonryLayout = new window.Masonry('.masonry', window.masonryOptions)
      document.querySelector('#load-more-oob').scrollIntoView({
        behavior: 'smooth',
        block: 'end'
      }) // This just re-aligns the window
    })
  </code></pre>
  
Tags: <javascript><plugin-sprig>
Title: Re-initialize / layout Masonry after a sprig change
ThreadText: # Re-initialize / layout Masonry after a sprig change
  
  (Asked by: username_0 on 2021-08-12)
  
  I've got Sprig set up to filter entries by a few categories. I'm trying to re-layout Masonry JS after this update takes place.
  
  For starters, I add Masonry to the window variable (defined as global in my JS module since I'm using node):
  
  `global.masonryLayout = new Masonry('.masonry', options)`
  
  Then, in my sprig component, I'm using a `<script>` tag that runs each time sprig loads entries, as per the docs:
  
  ```
  <script>
    if (window.masonryLayout) {
      window.masonryLayout.reloadItems() 
    }
  </script>
  
  ```
  
  I've got an example up and running, including console outputs each time the script runs. The Masonry class is there, but it just isn't laying out.
  
  1. <https://masonry.brycekirk.com/press>
  2. Filter by Source > The New York Times
  
  **Expected behaviour:** Items are loaded into a Masonry layout.
  
  **Actual behaviour:** No errors, but the layout is just normal columns, not Masonry (the 2nd row of articles are on their own line below the others).
  
  Right now I'm using `.reloadItems()`, which actually populates `colYs` and `cols`.
  
  `.layout()` leaves `colYs` and `cols` blank.
  
  What am I missing here??
  
  **EDIT: Working code based on username_1's answer**
  
  In my JS module:
  
  ```
  global.Masonry = Masonry
  global.masonryOptions = options
  global.masonryLayout = new Masonry('.masonry', options)
  
  ```
  
  In my sprig component:
  
  ```
  if (window.masonryLayout) {
      window.masonryLayout.destroy()
      window.masonryLayout = new window.Masonry('.masonry', window.masonryOptions)
  }
  
  ```
  
  **EDIT (JULY 2023):**
  This is much easier with htmx events. Just need to make sure to load the sprig script *before* your sprig component.
  
  JS module:
  
  ```
  global.Masonry = Masonry
  global.masonryOptions = options // These are just masonry options I've defined in an object variable
  
  window.onload = () => {
    global.masonryLayout = new Masonry('.masonry', options)
  }
  
  ```
  
  In my sprig component:
  
  ```
    htmx.on('htmx:beforeSwap', function(e) {
      window.masonryLayout.destroy()
    })
    
    htmx.on('htmx:afterSwap', function(e) {
      window.masonryLayout = new window.Masonry('.masonry', window.masonryOptions)
      document.querySelector('#load-more-oob').scrollIntoView({
        behavior: 'smooth',
        block: 'end'
      }) // This just re-aligns the window
    })
  
  ```
  
  **Tags:** javascript, plugin-sprig
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_1 on 2021-08-13. Score: 3 | Accepted answer
  
  This is oddly similar to [this question](https://craftcms.stackexchange.com/questions/38042/swiper-breaks-when-using-sprig).
  
  The problem is that your Sprig component includes the wrapper (the `div` with the `masonry` class), so reloading the Sprig component not only replaces the items, but also the wrapper itself. Masonry keeps a reference to this wrapper (the element masonry was initialized with) and uses it to find new children. Since the original wrapper was removed from the DOM, masonry can't find the new items.
  
  You can verify that by running `window.masonryLayout.getItemElements()` in your component script. It will always include the original six items, regardless of the current filters.
  
  The easiest solution would be to completely remove the Masonry instance using [.destroy()](https://masonry.desandro.com/methods.html#destroy) and initialize a new instance with the new wrapper whenever the component is reloaded. Not sure if that can be done with your current setup, since the Sprig component won't have access to the Masonry class. You could probably integrate the reinitialization logic in your compiled JS code by watching for DOM changes using a [Mutation Observer](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver). Or just expose the Masonry class to the window object in your Webpack build step, this way you can initialize a new instance in your Sprig component script.
  
  > **Comments:**
  > **username_0**: Thank you!! Tried using MutationObserver, but it wasn't detecting child node changes, though I think I could have gotten that sorted with some more time. For now, added the Masonry class to the window object and everything is working . Added working code to my answer.  
  
  ---
  
  