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', ...).
  • .first() / .last() — Dom7 1.6 doesn’t have these. Use .eq(0) / .eq($collection.length - 1) instead. Silently returns undefined → downstream methods throw.
  • .after(html) / .before(html)NOT in Dom7 1.6. Throws TypeError: n.parent().after is not a function. Dom7 has the inverted form insertAfter(selector) / insertBefore(selector) (called on the element-to-insert, takes the reference selector as arg). Or use native: refEl.parentNode.insertBefore(newEl, refEl.nextSibling).

$$.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).

Incident (2026-05-13)

Symptom: Mobile register popup’ta 422 validation hatalarında hiçbir error mesajı render edilmiyordu. Console’da Uncaught TypeError: n.parent().after is not a function. Root cause: loginRegisterAjax error handler $input.parent().after(err) kullanıyordu — Dom7 1.6’da .after() yok. İlk denemede de .find(...).first() kullanmıştım, o da yoktu. Fix: Native DOM: parentEl.parentNode.insertBefore(errEl, parentEl.nextSibling) + .eq(0). Bkz. 2026-05-13-mobile-register-popup-422-render-bug.

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.