Seth
Seth

Reputation: 984

UL tabs not clickable on Ipad

I've been tasked to determine why when an IPAD user attempts to use any tab bar it doesn't register as clickable at all. You can jab your finger at the button all day long, but as far as the IPAD is concerned it is just text it seems. A little project background is that its developed in asp MVC and uses LESS. I tested Safari 6 on my computer and a mac, the tabs are fine in those environments. Conflicting reports on whether the problem exists on the iPhone.

I did get my hands on a mac and an IPAD2 for the next few weeks. I've got web inspector running.

<div id="Tabs" class="tabContainer" >
    <ul>
        <li id="UserAdmin" class="selected">Users</li>
        <li id="CompanyAdmin">Companies</li>
        <li id="AdminMessages">Admin Messages</li>
    </ul>
</div>
<div class="tabContentDivider">
</div>
<div id="tabPlaceHolder" class="tabContent">
    <% Html.RenderPartial("ManageUsersPartial", Model, new ViewDataDictionary()); %>
</div>

JS:

$(function () {
    $('#Tabs ul li:not(.selected)').live('click', function () {
        $('#tooltip').remove();
        var selectedTab = $(this).attr('id');

        $('#Tabs ul li').removeClass('selected');
        $(this).addClass('selected');
        $(window).unbind('resize');
        loadRequest($('.tabContent'), global.baseUrl() + 'Admin/ShowTab/' + selectedTab, function () { });
    });
});

function loadRequest($container, resource, onComplete) {
    $.get(resource, function(result) {
        $result = $(result);

        if ($('#ExceptionPanel', $result).length > 0) {
            $('body').html(result);
        } else {
            $container.html(result);
            if (onComplete)
                onComplete();
        }
        $result.remove();
    });
}

What is missing here? Some CSS tag? I'd prefer not change the JavaScript. This is mature code.

Upvotes: 0

Views: 178

Answers (1)

matewka
matewka

Reputation: 10148

In iOS, the click event fires only on <a> elements (and possibly some form inputs). You should add the handler for touchstart event:

$('#Tabs ul li:not(.selected)').live('click touchstart', function () {
    //...
});

Still, the problem might be more complicated, since users will experience the handler behaviour even when they want to scroll the page and, for that purpose, tap the screen in the li area.
The ultimate solution would introduce touchstart as well as touchend and checking if $(window).scrollTop() or mouse (finger) position has changed. The code might look like this:

var scrollTop = false;
$('#Tabs ul').on('touchstart', 'li:not(.selected)', function (e) {
    e.preventDefault(); // IMPORTANT - this line fixes the Android bug but be aware that it might unintentionally prevent from scrolling the document
    scrollTop = $(window).scrollTop();
});
$('#Tabs ul').on('click touchend', 'li:not(.selected)', function () {
    // let's give it a 10px threshold, since the `click` is not always a pure point `click`
    // still, you might want to experiment with different thresholds
    if (false !== scrollTop && 10 < Math.abs(scrollTop - $(window).scrollTop())) {
        return scrollTop = false;
    }

    // ...rest of your code
});

EDIT:
I replaced e.pageY with $(window).scrollTop() - touch position might stay the same in relation to the document while scrolling the page.

EDIT 2:
You described the problem as related to the iOS but it would also show up on Android devices. There, you can add one more line to the touchstart handler: e.preventDefault(). I could mention that before but somehow I didn't. There is a known problem with touch events on Android, where sometimes the touchmove and touchend events refuse to fire. The added line of code resolves the problem.
Note that e.preventDefault() might prevent the document from scrolling. As I already mentioned, I have no possibility to test it.

EDIT 3:
According to Mike Barwick's comment, thanks @Mike for the edit. I also modified it a bit. The OP probably used .live method due to event delegation - I guess he adds <li> elements dynamically, so it would be better to pass .li:not(.selected) as the second argument of the .on method. More info HERE.

Upvotes: 4

Related Questions