Skip to content
mFilter
mFilter
Faceted filtering for MODX 3 with SEO URL support
  1. Extras
  2. mFilter
  3. Development
  4. JavaScript

JavaScript

Architecture and loading of mFilter JavaScript.

Architecture

mFilter uses a two-layer architecture:

┌─────────────────────────────────────────┐
│              mfilter.js                  │  ← Entry point, auto-init
├─────────────────────────────────────────┤
│              UI Layer                    │  ← DOM bindings for SSR
│            (FilterUI.js)                 │
├─────────────────────────────────────────┤
│             API Core                     │  ← Headless core
│  ApiClient.js │ FilterAPI.js │ hooks.js │
└─────────────────────────────────────────┘

API Core (Headless)

DOM-independent core:

  • ApiClient.js — HTTP client for server requests
  • FilterAPI.js — filter API methods
  • hooks.js — hook system for extension
  • mfilter.headless.js — init and export of window.mfilter

UI Layer (SSR)

DOM bindings for server-rendered markup:

  • FilterUI.js — form, results, pagination
  • mfilter.slider.js — noUiSlider integration
  • mfilter.js — auto-init

Files

FileSizePurpose
core/ApiClient.js~3 KBHTTP client
core/FilterAPI.js~5 KBAPI methods
modules/hooks.js~3 KBHook system
mfilter.headless.js~4 KBHeadless entry
ui/FilterUI.js~40 KBUI for SSR
mfilter.slider.js~8 KBnoUiSlider integration
mfilter.js~4 KBAuto-init

Loading

Automatic (default)

The mFilter plugin loads scripts on pages with filters.

Controlled by system setting mfilter.register_frontend:

  • true — auto-load (default)
  • false — disable, load manually

Manual loading

html
<!-- Base styles -->
<link rel="stylesheet" href="/assets/components/mfilter/css/web/mfilter.css">

<!-- API Core (required) -->
<script src="/assets/components/mfilter/js/web/core/ApiClient.js"></script>
<script src="/assets/components/mfilter/js/web/core/FilterAPI.js"></script>
<script src="/assets/components/mfilter/js/web/modules/hooks.js"></script>
<script src="/assets/components/mfilter/js/web/mfilter.headless.js"></script>

<!-- UI Layer (for SSR) -->
<script src="/assets/components/mfilter/js/web/ui/FilterUI.js"></script>
<script src="/assets/components/mfilter/js/web/mfilter.slider.js"></script>
<script src="/assets/components/mfilter/js/web/mfilter.js"></script>

Minimal set (Headless only)

For SPA apps:

html
<script src="/assets/components/mfilter/js/web/core/ApiClient.js"></script>
<script src="/assets/components/mfilter/js/web/core/FilterAPI.js"></script>
<script src="/assets/components/mfilter/js/web/modules/hooks.js"></script>
<script src="/assets/components/mfilter/js/web/mfilter.headless.js"></script>

Configuration

Global config via window.mfilterConfig:

html
<script>
window.mfilterConfig = {
    apiUrl: '/assets/components/mfilter/api.php',
    resourceId: 5,      // Category ID
    debug: false        // Debug mode
};
</script>

Initialization

Automatic

Elements with data-mfilter are initialized automatically:

html
<form data-mfilter data-mfilter-results=".results">
    <!-- filters -->
</form>

Manual

javascript
// Create instance
const instance = mfilterInit('#my-filter', {
    ajax: true,
    autoSubmit: true,
    autoSubmitDelay: 500
});

// Get existing instance
const filter = mfilterGet('my-filter');

// Destroy instance
mfilterDestroy('my-filter');

Data attributes

On container/form

AttributeDescription
data-mfilterMarker for auto-init
data-mfilter-resultsCSS selector for results block
data-mfilter-paginationCSS selector for pagination
data-mfilter-ajaxEnable AJAX (true/false)
data-mfilter-modeMode: form or instant
data-mfilter-auto-submitAuto-submit (true/false)
data-mfilter-delayAuto-submit delay (ms)
data-mfilter-seo-urlSEO URL (true/false)
data-mfilter-push-stateUpdate URL (true/false)
data-mfilter-scroll-to-resultsScroll to results
data-mfilter-scroll-offsetScroll offset (px)
data-mfilter-debugDebug mode
data-base-urlCategory base URL
data-resource-idResource ID

On elements

AttributeDescription
data-filterFilter key (on block)
data-range="min"Range min field
data-range="max"Range max field
data-mfilter-sliderMarker for noUiSlider
data-mfilter-sortSort element
data-mfilter-limitLimit selector element
data-mfilter-tplTemplate switch element

DOM events

EventDescriptiondetail
mfilter:readyAPI Core initialized{ mfilter }
mfilter:ui:readyUI initialized{ instances }
mfilter:contentLoadedNew content loaded (AJAX){ container }
mfilter:beforeSubmitBefore submit{ state, instance }
mfilter:afterSubmitAfter submit{ state, instance }
mfilter:successSuccess response{ response, instance }
mfilter:errorError{ error, instance }

Subscribing to events

javascript
document.addEventListener('mfilter:ui:ready', (e) => {
    console.log('Instances:', e.detail.instances);
});

document.addEventListener('mfilter:success', (e) => {
    console.log('Results:', e.detail.response);
});

Global objects

ObjectDescription
window.mfilterAPI Core (headless)
window.mfilterHooksHook system
window.MFilterUIUI constructor
window.MFilterSliderSlider API
window.mfilterInit()Create instance
window.mfilterGet()Get instance
window.mfilterDestroy()Destroy instance

noUiSlider

Range filters use noUiSlider.

Loading

Load noUiSlider before mFilter scripts:

html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nouislider@15/dist/nouislider.min.css">
<script src="https://cdn.jsdelivr.net/npm/nouislider@15/dist/nouislider.min.js"></script>

Customizing the slider

javascript
// Create slider manually
MFilterSlider.create('#my-slider', {
    start: [1000, 50000],
    range: { min: 0, max: 100000 },
    step: 100,
    tooltips: true,
    format: MFilterSlider.formats.currency
});

// Update range
MFilterSlider.updateRange('#my-slider', 0, 200000);

// Set values
MFilterSlider.set('#my-slider', [5000, 30000]);

Formats

javascript
MFilterSlider.formats.integer   // 12345
MFilterSlider.formats.float     // 123.45
MFilterSlider.formats.currency  // 12 345 ₽
MFilterSlider.formats.percent   // 50%

jQuery (optional)

If jQuery is loaded, the plugin is available:

javascript
$('#my-filter').mfilter({
    ajax: true,
    autoSubmit: true
});