Skip to main content

Fix iCheck Checkboxes Not Working on Tablets

Overview​

All iCheck-styled checkboxes and radio buttons in Ultimate POS stop responding to touch/tap on modern tablets (iPadOS 13+, some Android tablets). Tapping a checkbox does nothing — no visual change, no state change.

This affects every page that uses iCheck: POS settings, product forms, role permissions, manufacturing module, and more.

Problem Explanation​

Ultimate POS uses the iCheck library to style checkboxes and radio buttons. iCheck wraps each native <input> in a styled <div> and places an invisible <ins class="iCheck-helper"> overlay on top to capture mouse events.

iCheck detects mobile devices using this user agent check:

_mobile = /ipad|iphone|ipod|android|blackberry|windows phone|opera mini|silk/i.test(navigator.userAgent)

The problem: Starting with iPadOS 13 (2019), Apple changed the iPad's user agent to match desktop Safari — the string "iPad" is no longer present. This means _mobile = false on modern iPads.

When _mobile is false, iCheck's internal touchend handler executes return false, which calls both preventDefault() and stopPropagation(). This kills the synthetic click event that the browser would normally generate after a touch. Since iCheck only toggles checkboxes on click events (not on touchend), the checkbox never toggles.

In short:

  1. User taps checkbox on tablet
  2. touchend fires on the iCheck helper element
  3. iCheck's handler runs return false (because it thinks it's a desktop browser)
  4. preventDefault() prevents the browser from generating a synthetic click
  5. No click = no toggle = checkbox appears broken

Solution​

Add a capturing-phase touchend listener that fires before iCheck's own handler, toggles the checkbox, and blocks iCheck from interfering. A second capturing-phase click listener blocks the synthetic click (on devices where it still fires) to prevent double-toggling.

Step 1: Locate the iCheck Initialization​

Open public/js/app.js and find the iCheck initialization block:

public/js/app.js
//initialize iCheck
$('input[type="checkbox"].input-icheck, input[type="radio"].input-icheck').iCheck({
checkboxClass: 'icheckbox_square-blue',
radioClass: 'iradio_square-blue',
});

Step 2: Add the Touch Fix​

Paste the following code immediately after the iCheck initialization block:

public/js/app.js
// Fix iCheck not responding to touch/tap on tablets (iPadOS 13+, etc).
// Root cause: iCheck detects mobile via UA string, but modern iPads report
// as desktop Safari. When _mobile=false, iCheck does "return false" on
// touchend which kills the synthetic click — so the checkbox never toggles.
// Fix: capturing-phase touchend toggles the checkbox, then a capturing-phase
// click handler blocks the synthetic click to prevent double-toggle.
(function() {
var _iCheckTouched = false;

function findICheckWrapper(target) {
var $target = $(target);
var $wrapper = $target.closest('.icheckbox_square-blue, .iradio_square-blue');
if (!$wrapper.length) {
var $label = $target.closest('label');
if ($label.length) {
$wrapper = $label.find('.icheckbox_square-blue, .iradio_square-blue');
}
}
return $wrapper;
}

document.addEventListener('touchend', function(e) {
var $wrapper = findICheckWrapper(e.target);
if ($wrapper.length) {
var $input = $wrapper.find('input');
if ($input.length && !$input.prop('disabled')) {
e.stopPropagation();
_iCheckTouched = true;
setTimeout(function() { _iCheckTouched = false; }, 500);
if ($wrapper.hasClass('checked')) {
$input.iCheck('uncheck');
} else {
$input.iCheck('check');
}
}
}
}, true);

// Block synthetic click that fires ~300ms after touchend to prevent
// iCheck's click handler from double-toggling the checkbox back.
document.addEventListener('click', function(e) {
if (_iCheckTouched && findICheckWrapper(e.target).length) {
e.stopPropagation();
e.preventDefault();
}
}, true);
})();

That's it. Only one file needs to change (public/js/app.js). The fix is global — it applies to all iCheck checkboxes and radio buttons across the entire application.

How It Works​

The fix uses two capturing-phase event listeners registered on document. Capturing-phase listeners fire before any target or bubbling-phase handlers (including iCheck's directly-bound handlers).

touchend listener (capturing phase)​

  1. User taps a checkbox on a tablet
  2. Our capturing listener fires before iCheck's handler
  3. Finds the iCheck wrapper (.icheckbox_square-blue or .iradio_square-blue)
  4. Toggles the checkbox using iCheck's API ($input.iCheck('check') / $input.iCheck('uncheck'))
  5. Calls e.stopPropagation() to prevent iCheck's own touchend handler from running
  6. Sets a _iCheckTouched flag for 500ms

click listener (capturing phase)​

  1. On some devices/browsers, a synthetic click still fires ~300ms after the touch
  2. If _iCheckTouched is true, the click is blocked with stopPropagation() + preventDefault()
  3. This prevents iCheck's click handler from toggling the checkbox a second time (double-toggle)

Why this doesn't affect desktop​

Desktop browsers don't fire touchend events (only mouse events), so the touchend listener never triggers. Desktop behavior is completely unchanged.

Why this works for dynamically added checkboxes​

The listeners are on document (not on individual elements), and use $(e.target).closest() to find the wrapper. This works for elements added to the DOM at any time — no re-initialization needed.

How to Test​

  1. Open your application in Chrome
  2. Press F12 to open DevTools
  3. Click the device toggle toolbar button (or press Ctrl+Shift+M)
  4. Select a tablet device (e.g. iPad Air)
  5. Hard-refresh the page with Ctrl+Shift+R (to bypass cache)
  6. Navigate to any page with checkboxes (e.g. product form, role permissions, POS settings)
  7. Tap a checkbox — it should toggle on and off
tip

Always hard-refresh (Ctrl+Shift+R) after making changes to app.js to ensure the browser loads the updated file.

Affected Versions​

  • Ultimate POS: v6.x (all versions using iCheck)
  • Affected tablets: iPadOS 13+ (2019+), Windows tablets, some Android tablets with desktop-mode user agents
  • Not affected: Desktop browsers, older iPads (iOS 12 and below), phones

💛 Support this project

Premium Login