Proje: Okul Platform · Hub: Okul Platform — Conventions

Dom7 (mobile $$) is NOT jQuery — selector/method gotchas

resources/assets/scripts/mobile/app.js uses $$ which is Dom7 (Framework7’s jQuery-lite), not jQuery. Most APIs look the same, but several jQuery-only conveniences are missing and throw at runtime.

Unsupported pseudo-selectors

Dom7 delegates .is() / .filter() to native Element.matches(), which only accepts valid CSS selectors. jQuery-invented pseudos throw DOMException: Element.matches: ':X' is not a valid selector.

Known breakers:

  • :visible — use window.getComputedStyle(el).display !== 'none'
  • :hidden — use window.getComputedStyle(el).display === 'none' or .css('display') === 'none'

Safe (standard CSS): :checked, :disabled, :focus, :first-child, etc.

Unsupported methods

  • .get(index) — use array access $$collection[index] instead (Dom7 doesn’t implement .get()). Throws TypeError: $form.find(...).get is not a function.
  • .removeData(key) — use .data(key, null) instead (Dom7 .data() accepts arbitrary values including null).
  • $$.trim — use native str.trim() (Dom7 has no $$.trim utility).
  • $$(function() {...}) document-ready shorthand — not guaranteed. Use document.readyState check or document.addEventListener('DOMContentLoaded', ...).

$$.ajax() is callback-based, NOT promise-returning

Dom7’s $$.ajax() does not return a deferred/promise. .always() / .done() / .fail() chain throws TypeError: $$.ajax(...).always is not a function.

Use options callbacks instead:

$$.ajax({
    type: 'POST',
    url: url,
    data: data,
    dataType: 'json',
    success: function (data) { /* .done equivalent */ },
    error: function (xhr) { /* .fail equivalent — xhr, not responseJSON auto-populated */ },
    complete: function () { /* .always equivalent — cleanup, always runs */ }
});

Also: Dom7’s error handler receives raw xhr; xhr.responseJSON may not be set automatically. Parse xhr.responseText yourself (try { JSON.parse(xhr.responseText) }).

Supported (verified in this codebase)

  • .data(key, value) including non-string values (timeouts, intervals, objects) — see existing scrollTimeout pattern around app.js:1312.
  • .css('prop', val) as setter and getter.
  • .on(event, selector, handler) event delegation.
  • .closest(), .find(), .each(), .attr(), .val().
  • $$.ajax() with dataType: 'json', always/done/fail promise chain.

Incident (2026-04-14)

Symptom: Mobile OTP login failed on submit with Uncaught DOMException: Element.matches: ':visible' is not a valid selector. Root cause: I wrote $form.find('.otp-code-group').is(':visible') out of jQuery habit. Fix: Native computed-style check. Also prophylactically swapped removeDatadata(…, null).

Rule

Before using any selector/method in mobile app.js, ask: “is this standard CSS / standard DOM, or a jQuery convenience?” If the latter, find a native or Dom7-supported equivalent. Desktop general.js runs real jQuery — these gotchas only apply to mobile.