Search code examples
javascriptcssclicktouch-event

Why can't I click on links on mobile device?


I am developing a site (saulesinterjerai.lt) Everything works now, except on mobile device the links are not clickable, instead it goes through to other layers..

How do I disable this behavior from the code? (not on browser)

The code that I wrote is here, however some parts of code I copied from the web (everything in "dep" folder if you know how to use dev tools). I also do suspect it could be problem with pjax or the proprietary "zoomable" module, but I can't find why..:

var pjax = new Pjax({ selectors: ["head title", "body"] })
var Site;

var attach_menu_control = function() {
  var $sidebar = document.querySelector('.sidebar')
  var $sidebar_content = $sidebar.querySelector('.content')
  var $menu_opener = $sidebar.querySelector('.menu-closed')

  var hide_menu = function() {
    $sidebar_content.style.display = 'none'
    $menu_opener.style.display = 'block'
    $sidebar.style.width = '40px'
  }

  var show_menu = function() {
    $sidebar_content.style.display = 'block'
    $menu_opener.style.display = 'none'
    $sidebar.style.width = '270px'
  }

  var click_handler = function(e){
    // e.preventDefault()
    var width = (window.innerWidth > 0) ? window.innerWidth : screen.width;
    if ($sidebar_content.style.display == 'none') {
      show_menu()
    } else if (width <= 724) {
      hide_menu()
    }
  }

  if ( !is_touch_device() ) $sidebar.addEventListener('click', click_handler)
  $sidebar.addEventListener('touchend', click_handler)
  var event = new Event('click');
  $sidebar.dispatchEvent(event)
}
attach_menu_control()

document.addEventListener('DOMContentLoaded', reloadCarbon)
document.addEventListener('pjax:complete', reloadCarbon)
document.addEventListener('pjax:send', resetCarbon)

// Animations

document.addEventListener('pjax:send', function(){
  var $main = document.querySelector('main')
  $main.style.opacity = 0
})

document.addEventListener('pjax:complete', function(){
  var $main = document.querySelector('main')
  $main.style.visibility = 'hidden'
  $main.style.opacity = 0
  setTimeout(function(){
    document.querySelector('main').style.visibility = 'visible'
    document.querySelector('main').style.opacity = 1
    attach_menu_control()
  }, 10)
})

function hasClass(element, cls) {
    return (' ' + element.className + ' ').indexOf(' ' + cls + ' ') > -1;
}

function is_touch_device() {
  return 'ontouchstart' in window        // works on most browsers 
      || navigator.maxTouchPoints;       // works on IE10/11 and Surface
}

CSS:

    .menu-closed {
        font-size: 30px;
      position: absolute;
      writing-mode: vertical-lr;
      top: 50%;
      padding-left: 14px;
      cursor: pointer;
    }

    .sidebar {
        transition: width 0.2s;
        overflow-x: hidden;
        z-index: 10000;
        backface-visibility: hidden;
    }

    .sidebar .content {
        width: 270px;
    }


    @media only screen and (max-width: 724px) {
        .sidebar {
            background-color: rgba(245, 245, 245, 0.87);
        }

        .sidebar h1 {
            font-size: 36px;
        }

        .sidebar header h2 {
            font-size: 28px;
        }

        .language {
            font-size: 16px;
        }

        .sidebar nav > ul > li {
            line-height: 36px;
        }

        .sidebar nav > ul.siteLinks a, 
        .sidebar nav > ul > li > a {
            font-size: 36px;
            line-height: 36px;
        }

        main {
            padding-left: 40px;
        }

        /* Force sideback to be in closed mode when new page is opened */
        .sidebar {
            width: 40px;
        }

        .sidebar .content {
            display: none;
        }
    }

    /* Emulate fixed positionin */

    html {
        position: absolute;
        height: 100%;
        width: 100%;
        overflow: hidden;
    }

    body {
        height: 100%;
        overflow: auto;
    }

    .fixed {
        position: absolute;
    }

Solution

  • I think this is due to this function, which gets called:

     var click_handler = function(e){
        e.preventDefault()
        var width = (window.innerWidth > 0) ? window.innerWidth : screen.width;
        if ($sidebar_content.style.display == 'none') {
          show_menu()
        } else if (width <= 724) {
          hide_menu()
        }
      }
    

    e.preventDefault() prevents the browser to perform the default action, here: to switch pages and follow the URL in the link.

    I guess you have to be careful on which item you attach this handler. This kind of handler does not feel very natural on a link.

    This was highlighted by the Chrome console's warnings:

    Ignored attempt to cancel a touchend event with cancelable=false, for example because scrolling is in progress and cannot be interrupted.
    click_handler @ app.js:34
    

    EDIT

    I see a lot of handlers attached to your elements, and I don't understand each one's role (i don't know the pjax lib for instance), I think one is responsible of the loss of the expected behavior. If I were you, maybe I'd try to make it simpler and rewrite the menu system and its integration in the webpage.

    If you can't (or don't want to) refactor the page, maybe don't try to fix it but try a workaround.

    What I would do is to add a transparent grey backdrop when the menu is open, to show the user the rest of the screen is disabled (or full transparent and when the user clicks/taps on it, it closes the menu to go back to the main content). When the menu is open, you set on the main content the following css:

    pointer-events: none
    

    This will prevent the pictures/content under the menu to receive events, and only the menu should get click/tap events. Hope this helps!