Hover Intent + Touch - Dropdown Menu

A touch-enabled jQuery dropdown menu with hover intent for mouse events.

by bizamajig

HTML

<!-- IF YOU WANT TO TRY THIS ON A TOUCH SCREEN DEVICE YOU WILL NEED TO VIEW WITHIN THE FULL SCREEN PREVIEW -->

<ul class="menu">
    <li class="dropdown">
        <a href="#" class="toplevel">This is a dropdown menu</a>
        <ul class="submenu">
            <li><a href="#">Lorem</a></li>
            <li><a href="#">Ipsum</a></li>
            <li><a href="#">Doler</a></li>
            <li><a href="#">Sit amet</a></li>
        </ul>
    </li>
</ul>

CSS

.menu {
    width:200px;
    list-style-type:none;
}
    .toplevel {
        display:block;
        padding:0 10px;
        line-height:2;
        color:#fff;
        text-decoration:none;
        background:#f00;
    }

    /* Dropdowns */
    .dropdown {
        position:relative;
        z-index:200;
    }
        .submenu {
            position:absolute;
            top:0;
            left:0;
            display:none;
            width:200px;
            list-style-type:none;
            padding-top:2em;
            z-index:200;
        }
            .hover .submenu {
                display:block;
            }
            .submenu a {
                display:block;
                padding:5px;
                color:#fff;
                text-decoration:none;
                background:navy;
            }

JavaScript

$(function()
{
    var $dropdowns = $('li.dropdown'); // Specifying the element is faster for older browsers

    /**
     * Mouse events
     *
     * @description Mimic hoverIntent plugin by waiting for the mouse to 'settle' within the target before triggering
     */
    $dropdowns
        .on('mouseover', function() // Mouseenter (used with .hover()) does not trigger when user enters from outside document window
        {
            var $this = $(this);

            if ($this.prop('hoverTimeout'))
            {
                $this.prop('hoverTimeout', clearTimeout($this.prop('hoverTimeout')));
            }

            $this.prop('hoverIntent', setTimeout(function()
            {
                $this.addClass('hover');
            }, 250));
        })
        .on('mouseleave', function()
        {
            var $this = $(this);

            if ($this.prop('hoverIntent'))
            {
                $this.prop('hoverIntent', clearTimeout($this.prop('hoverIntent')));
            }

            $this.prop('hoverTimeout', setTimeout(function()
            {
                $this.removeClass('hover');
            }, 250));
        });

    /**
     * Touch events
     *
     * @description Support click to open if we're dealing with a touchscreen
     */
    if ('ontouchstart' in document.documentElement)
    {
        $dropdowns.each(function()
        {
            var $this = $(this);

            this.addEventListener('touchstart', function(e)
            {
                if (e.touches.length === 1)
                {
                    // Prevent touch events within dropdown bubbling down to document
                    e.stopPropagation();

                    // Toggle hover
                    if (!$this.hasClass('hover'))
                    {
                        // Prevent link on first touch
                        if (e.target === this || e.target.parentNode === this)
                        {
                           ...