nuxt: added client side js is not executed at first page load

Viewed 742

I added photoswipe (v5) to a component like this:

mounted () {
    if (!document.getElementById('psw')) {
      const script = document.createElement('script')
      script.id = 'psw'
      script.type = 'module'
      script.innerHTML = `
          import PhotoSwipeLightbox from '/js/photoswipe/photoswipe-lightbox.esm.min.js';
          const lightbox = new PhotoSwipeLightbox({ 
            gallerySelector: '#gallery--productimages',
            childSelector: 'a',
            pswpModule: '/js/photoswipe/photoswipe.esm.min.js'
          });
          lightbox.init();
        `
      document.head.appendChild(script)
    }
  },

The photoswipe files are in the static folder. The script tag is added to the DOM, but photoswipe does not execute when the page is loaded the first time (from internal route link and direct server call). When I gob back an open the page again by the internal link, or refresh the page, it works. I only want to use photoswipe in this component. How can I make it work for the first page load also?

2 Answers

I don't think that is a good way to inject scripts in your app. I usually load external packages this way:

First I create PhotoSwipe.js under plugins folder:

import PhotoSwipe from '/js/photoswipe/photoswipe.esm.min.js'
import PhotoSwipeLightbox from '/js/photoswipe/photoswipe-lightbox.esm.min.js'

export default (context, inject) => {
  inject('PhotoSwipe', PhotoSwipe)
  inject('PhotoSwipeLightbox', PhotoSwipeLightbox)
}

Then I load it in the plugins attribute inside nuxt.config.js:

plugins: [
  { src: '~/plugins/PhotoSwipe', mode: 'client' }
]

Then you can use it in any page or component this way:

data () {
  return {
    lightbox: {}
  }
},

mounted () {
  this.loadPhotoSwipeLightBox()
},
beforeDestroy () {       // added
  this.closeLightBox()
},
methods: {
  loadPhotoSwipeLightBox () {
    const options = {
      // define the images to load in lightbox
      dataSource: [
        {
          src: 'https://source.unsplash.com/Volo9FYUAzU/1620x1080',
          w: 1600,
          h: 1600,
          alt: 'test image 1'
        }
      ],
      pswpModule: this.$Photoswipe
    }
    this.lightbox = new this.$PhotoswipeLightbox(options)
    this.lightbox.init()

/*    const lightbox = new this.$PhotoSwipeLightbox({ 
      gallerySelector: '#gallery--productimages',
      childSelector: 'a',
      pswpModule: this.$PhotoSwipe
    });

    lightbox.init();
*/
  },
  openLightBox () {    // this method should be called onClick on the thumbnailimage
    this.lightbox.loadAndOpen(0)  // exchange 0 with the dynamic index of the thumbnail => to do 
  },
  closeLightBox () {
    if (this.lightbox) {
      this.lightbox.destroy()
    }
  }
}

EDIT: Use injected this.$PhotoSwipe in pswpModule instead of a url. EDIT: Added closeLightBox() to close the Lightbox also by clicking the browser back-button, using dataSource-Array to get the images loaded in lightbox

To follow what you have, I think below should work. This way I think you can enclose all relevant code in your component instead of spread theme everywhere like in nuxt plugins and nuxt.config.js.

mounted () {
  const photoswipe = '/js/photoswipe/photoswipe-lightbox.esm.min.js'
  // Prevent the script being added duplicated when have multiple photoswipe components on one page.
  const existingScript = document.head.querySelector(`script[src='${photoswipe}']`)
  if (existingScript) {
    existingScript.addEventListener('load', this.init)
  } else {
    const photoswipeScript = document.createElement('script')
    photoswipeScript.addEventListener('load', this.init)
    photoswipeScript.setAttribute('src', photoswipe)
    photoswipeScript.setAttribute('type', module)
    document.head.appendChild(photoswipeScript)
  }
},
methods: {
  init () {
    const lightbox = new PhotoSwipeLightbox({
      // To support multiple components on one page, you may need to use `ref`, add `ref="gallery"` on the same element of your id `#gallery--productimages`
      gallerySelector: this.$refs.gallery,
      childSelector: 'a',
      pswpModule: '/js/photoswipe/photoswipe.esm.min.js'
    });
    lightbox.init();
  }
}
Related