0

I'm working on a custom JavaScript setup for a Shopify development store. I use Vite to bundle everything into a single JS file that gets included via:

{{ 'bundle.js' | asset_url | script_tag }}

The Problem On some page loads, all logs and functionality behave as expected.

On other loads (without code changes), my module logs still appear (showing the script ran), but core functionality breaks silently.

Most commonly, event listeners like click and change don’t fire, or DOM-dependent code fails to execute.

This only happens sometimes, seemingly depending on when and how Shopify renders DOM elements, or how third-party apps modify them.

I've wrapped my initialization in DOMContentLoaded, but it doesn't reliably fix the issue.

Tried Solutions

  • Removing Vite entirely and using plain tags – same issue.
  • Switching to import maps – no improvement.
  • Adding a version parameter to the script URL (?v=123) and dynamically parsing it to bypass Shopify caching – no effect.

Example 1: Overall JS Initialization Logic I initialize many features and utilities from different modules, like so:

import { initA, initB } from './module-a.js';
import { initC, initD } from './module-b.js';
import { setupLightbox } from './lightbox.js';

const initialize = () => {
  initA();
  initB();
  initC();
  initD();

  if (window.location.href.includes('custom-config')) {
    initCustomFeature();
  }
};

document.addEventListener('DOMContentLoaded', () => {
  console.log('DOM loaded');
  initialize();

  setupLightbox({
    selector: '.lightbox',
    loop: true,
    touchNavigation: true,
  });
});

Even though DOMContentLoaded fires and logs appear, some modules fail to work properly — especially ones relying on DOM elements added by Shopify sections or apps.

Module with Click + Change Event Listeners This is a simplified version of a module where the issue appears:

export const exampleFeature = {
  initialize() {
    this.sizeContainer = document.querySelector('.size-options');
    this.countSelector = document.querySelector('.count-selector');

    this.setupEventListeners();
    this.updateCountOptions('4');
  },

  setupEventListeners() {
    this.sizeContainer?.addEventListener('click', (e) => {
      const selected = e.target.closest('.size-option');
      if (!selected) return;

      document.querySelectorAll('.size-option').forEach((el) => el.classList.remove('active'));
      selected.classList.add('active');

      this.updateCountOptions(selected.dataset.size);
    });

    this.countSelector?.addEventListener('change', (e) => {
      const value = e.target.value;
      this.updateSelectionUI(value);
    });
  },

  updateCountOptions(size) {
    // Logic to update count options
  },

  updateSelectionUI(value) {
    // Update UI or internal state
  },
};

This module often fails silently: either the event listeners don’t fire, or the DOM elements return null when selected — despite DOMContentLoaded supposedly guaranteeing that the DOM is ready.

2
  • 2
    DOMContentLoaded guarantees that the initial HTML the browser received from the server, has finished parsing. But the content you're trying to target, is probably not part of that - but gets added to the document later on, by other JavaScript components. You should probably use event delegation - freecodecamp.org/news/event-delegation-javascript, medium.com/@karthickrajaraja424/… Commented Jun 4 at 9:34
  • @C3roe Thank you for your answer. Their is no event listeners that i setup where the classes are not part of the intial HTML... Commented Jun 4 at 17:33

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.