Shopify price filter slider working on desktop but not on mobile

Topic summary

A developer is implementing a custom price range slider in Shopify’s Dawn 15.2 theme that functions correctly on desktop but fails on mobile devices.

Core Issue:

  • The slider thumbs are movable on mobile, but the input event listener doesn’t trigger (confirmed by missing console logs)
  • Manual input field changes do update the slider correctly
  • The slider is visible in both desktop and mobile views

Technical Context:

  • The implementation uses a custom PriceFilter JavaScript class with event listeners on range sliders
  • Main difference between views: filter_type is ‘drawer’ on mobile vs ‘horizontal’ on desktop
  • Developer suspects the event listener this.minSlider.addEventListener("input", ...) isn’t firing on mobile

Current Status:

  • Troubleshooting attempts with ChatGPT haven’t resolved the issue
  • Code snippets for both the JavaScript class and Liquid template have been shared
  • The developer is seeking help from others who may have encountered similar mobile touch event issues with custom Shopify filters

The discussion remains open with no solution yet provided.

Summarized with AI on October 31. AI used: claude-sonnet-4-5-20250929.

I am currently creating a shopify webshop in the dawn 15.2 theme. I was able to add a slider for my price filter on the collection page by editing the code: https://bricksafterwork.com/collections/all

It seems to be working fine on desktop, but when using it on mobile, the slider doesn’t seem to work. I am able to move the thumbs, but it doesn’t change the filter. If I change the input fields however, the slider does update…

I was trying to troubleshoot using chat gpt but I can’t seem to figure it out. The way I see it the probles is that the

this.minSlider.addEventListener(“input”, (event) => {
console.log(" :bullseye: Min slider wordt bewogen…");
this.updateInputsFromSlider();
});

Is not triggered, but I can’t figure out why (the log is not showing when moving the thumb on mobile while it does show on desktop). The only change I can see between desktop and mobile is that filter_type == ‘drawer’ in stead of ‘horizontal’, but I don’t see how this can change the application of the slider since the slider is visible in the mobile version as well.

Anybody found something similar?

Here you can find the js script used for the pricefilter and below the facets.liquid code.

class PriceFilter {
  constructor() {
    this.minInput = document.getElementById("price-min");
    this.maxInput = document.getElementById("price-max");
    this.minSlider = document.getElementById("price-slider-min");
    this.maxSlider = document.getElementById("price-slider-max");

    if (this.minInput && this.maxInput && this.minSlider && this.maxSlider) {
      console.log("All elements are loaded.");
      this.initializeValues();
      this.addEventListeners();
    } else {
      console.error('One or more elements are missing');
    }
    console.log("1 Prijsfilter gemaakt");
  }

  initializeValues() {
  
      let minVal = parseFloat(this.minInput.value) || parseFloat(this.minSlider.min);
      let maxVal = parseFloat(this.maxInput.value) || parseFloat(this.maxSlider.max);
  
      minVal = Math.max(minVal, parseFloat(this.minSlider.min));
      maxVal = Math.min(maxVal, parseFloat(this.maxSlider.max));
  
      this.minSlider.value = minVal;
      this.maxSlider.value = maxVal;
      this.minInput.value = minVal;
      this.maxInput.value = maxVal;

      // Forceer pointer-events: auto op mobiel
      this.minSlider.style.pointerEvents = "auto";
      this.maxSlider.style.pointerEvents = "auto";
  
      // **FORCEER de update van de slider op mobiel**
      setTimeout(() => {
          this.updateInputsFromSlider();
      }, 500);
      console.log("2 Prijsfilter geinitializeerd");
  }

  addEventListeners() {
    console.log(" addEventListeners() wordt aangeroepen...");

    if (!this.minSlider || !this.maxSlider) {
      console.error(" Min of Max slider bestaat niet!");
      return;
    }
  
    console.log(" Min slider gevonden:", this.minSlider);
    console.log(" Max slider gevonden:", this.maxSlider);
  
    this.minSlider.addEventListener("input", (event) => {
      console.log("? Min slider verandert:", event.target.value);
      this.updateInputsFromSlider();
    });
  
    this.maxSlider.addEventListener("input", (event) => {
      console.log("? Max slider verandert:", event.target.value);
      this.updateInputsFromSlider();
    });
  
    console.log(" Event listeners toegevoegd aan sliders.");
    // Sliders filteren automatisch na een korte tijd (bij verandering)
    this.minSlider.addEventListener("input", (event) => {
      console.log(" Min slider wordt bewogen...");
      console.log(" Oude waarde:", event.target.getAttribute("value"));
      console.log(" Nieuwe waarde:", event.target.value);
      this.updateInputsFromSlider();
    });
    
    this.minSlider.addEventListener("change", () => {
        console.log(" [DEBUG] Slider min CHANGE event!");
        this.updateInputsFromSlider();
    });

    this.maxSlider.addEventListener("input", () => {
        console.log(" [DEBUG] Slider max wordt bewogen!");
        this.updateInputsFromSlider();
    });
    this.maxSlider.addEventListener("change", () => {
        console.log(" [DEBUG] Slider max CHANGE event!");
        this.updateInputsFromSlider();
    });

    // Inputvelden moeten geen filter meer toepassen tijdens het typen
    this.minInput.removeEventListener("input", this.validateMinInput);
    this.maxInput.removeEventListener("input", this.validateMaxInput);

    // Pas de slider pas aan bij 'Enter'
    this.minInput.addEventListener("keydown", (event) => {
      if (event.key === "Enter") {
        event.preventDefault();
        this.updateSliderFromInputs(true);
        this.applyFilter();
      }
    });

    // Filterknop om handmatig de waarden toe te passen
    const applyFilterButton = document.getElementById("apply-price-filter");
    if (applyFilterButton) {
      applyFilterButton.addEventListener("click", () => {
        this.updateSliderFromInputs(true);
      });
    }
    document.addEventListener("filters-updated", () => {
      console.log("Filters zijn geüpdatet, prijsfilter wordt opnieuw gesynchroniseerd...");
      this.syncPriceUIWithFilters();
    });
    this.minSlider.addEventListener("input", () => {
        console.log(" Slider min wordt bewogen op mobiel!");
        this.updateInputsFromSlider();
    });
    
    this.maxSlider.addEventListener("input", () => {
        console.log(" Slider max wordt bewogen op mobiel!");
        this.updateInputsFromSlider();
    });
  }

  validateMinInput() {
    let minVal = parseInt(this.minInput.value) || parseInt(this.minSlider.min);
    let maxVal = parseInt(this.maxInput.value) || parseInt(this.maxSlider.max);

    if (minVal > maxVal) {
      minVal = maxVal;
      this.minInput.value = minVal;
    }

    if (minVal < parseInt(this.minSlider.min)) {
      minVal = parseInt(this.minSlider.min);
    }

    this.minInput.value = minVal;
    // De slider en filter worden niet automatisch aangepast
  }

  validateMaxInput() {
    let minVal = parseInt(this.minInput.value) || parseInt(this.minSlider.min);
    let maxVal = parseInt(this.maxInput.value) || parseInt(this.maxSlider.max);

    if (maxVal < minVal) {
      maxVal = minVal;
      this.maxInput.value = maxVal;
    }

    if (maxVal > parseInt(this.maxSlider.max)) {
      maxVal = parseInt(this.maxSlider.max);
    }

    this.maxInput.value = maxVal;
    // De slider en filter worden niet automatisch aangepast
  }

 updateSliderFromInputs(applyChanges = false) {
      let minVal = parseFloat(this.minInput.value);
      let maxVal = parseFloat(this.maxInput.value);
  
      if (minVal > maxVal) {
        minVal = maxVal;
        this.minInput.value = minVal;
      }
  
      if (maxVal < minVal) {
        maxVal = minVal;
        this.maxInput.value = maxVal;
      }
  
      if (applyChanges) {
        this.minSlider.value = minVal;
        this.maxSlider.value = maxVal;
        
        this.applyFilter(); // Pas filter correct toe
      }
  }

  updateInputsFromSlider() {
      let minVal = Math.floor(parseFloat(this.minSlider.value));
      let maxVal = Math.ceil(parseFloat(this.maxSlider.value));
      
      this.minInput.value = minVal;
      this.maxInput.value = maxVal;
  
      this.minInput.dispatchEvent(new Event("change", { bubbles: true }));
      this.maxInput.dispatchEvent(new Event("change", { bubbles: true }));
      console.log(" updateInputsFromSlider() wordt aangeroepen...");
      console.log(" Min slider waarde:", this.minSlider.value);
      console.log(" Max slider waarde:", this.maxSlider.value);
  }

  applyFilter() {
      const params = new URLSearchParams(window.location.search);
  
      // Haal de huidige prijswaarden op
      const minPrice = this.minInput.value;
      const maxPrice = this.maxInput.value;
  
  
      // Controleer of de prijsfilter is aangepast door de gebruiker
      const minChanged = minPrice != this.minSlider.min;
      const maxChanged = maxPrice != this.maxSlider.max;
  
  
      // Alleen toevoegen als ze echt zijn aangepast
      if (minChanged) {
          params.set("filter.v.price.gte", minPrice);
      } else {
          params.delete("filter.v.price.gte"); // Verwijder als niet aangepast
      }
  
      if (maxChanged) {
          params.set("filter.v.price.lte", maxPrice);
      } else {
          params.delete("filter.v.price.lte"); // Verwijder als niet aangepast
      }
  
      // Update de URL zonder onnodige prijsfilters
      window.history.replaceState({}, "", `${window.location.pathname}?${params.toString()}`);
  
      // Trigger de filter
      const filterForm = document.querySelector("form.filters-form");
      if (filterForm) {
          filterForm.dispatchEvent(new Event("submit", { bubbles: true }));
      } else {
          location.reload();
      }
  }

  syncPriceUIWithFilters() {
  
    const urlParams = new URLSearchParams(window.location.search);
    let minVal = urlParams.get("filter.v.price.gte") || this.minSlider.min;
    let maxVal = urlParams.get("filter.v.price.lte") || this.maxSlider.max;
  
    // Zorg ervoor dat de waarden correct worden ingesteld
    this.minInput.value = minVal;
    this.maxInput.value = maxVal;
    this.minSlider.value = minVal;
    this.maxSlider.value = maxVal;
  }
}
{% comment %}
  Renders facets (filtering and sorting)

  Accepts:
  - results: {Object} Collection or Search object
  - enable_filtering: {Boolean} Show filtering when true
  - enable_sorting: {Boolean} Show sorting when true
  - filter_type: {String} Type of filter
  - paginate: {Object}

  Usage:
  {% render 'facets', results: collection, enable_filtering: true, enable_sorting: true, filter_type: 'vertical', paginate: paginate %}
{% endcomment %}

{{ 'component-show-more.css' | asset_url | stylesheet_tag }}
{{ 'component-swatch-input.css' | asset_url | stylesheet_tag }}
{{ 'component-swatch.css' | asset_url | stylesheet_tag }}

{%- liquid
  assign sort_by = results.sort_by | default: results.default_sort_by
  assign total_active_values = 0
  assign default_presentation = 'text'
  if results.url
    assign results_url = results.url
  else
    assign terms = results.terms | escape
    assign results_url = '?q=' | append: terms | append: '&options%5Bprefix%5D=last&sort_by=' | append: sort_by
  endif
-%}

  {%- if filter_type == 'vertical' or filter_type == 'horizontal' -%}
    
  {%- endif -%}
  {% comment %}  Drawer and mobile filter {% endcomment %}
  

  

    {%- for filter in results.filters -%}
      {%- for value in filter.active_values -%}
        
      {%- endfor -%}

      {%- if filter.type == 'price_range' -%}
        {% assign min = filter.min_value.value %}
        {% assign max = filter.max_value.value %}
        {%- if min != null or max != null -%}
          
        {%- endif -%}
      {%- endif -%}
    {%- endfor -%}
    
  

  {% comment %} Sort, product count and filter pills at the end when filter is type of Drawer for the correct tabbing order {% endcomment %}
  {%- if enable_sorting and filter_type == 'drawer' -%}
    
  {%- endif -%}
  

    ## 
      
        {%- if results.results_count -%}
          {{ 'templates.search.results_with_count' | t: terms: results.terms, count: results.results_count }}
        {%- elsif results.products_count == results.all_products_count -%}
          {{ 'products.facets.product_count_simple' | t: count: results.products_count }}
        {%- else -%}
          {{
            'products.facets.product_count'
            | t: product_count: results.products_count, count: results.all_products_count
          }}
        {%- endif -%}
      
    
    {%- render 'loading-spinner' -%}
  

  {%- if filter_type == 'drawer' -%}
    
  {%- endif -%}