Controlling vertical scrolling and horizontal scrolling in a list in an iOS web application using JavaScript

I am creating a web application specifically designed for phones (primarily iPhone, but Android and WP are on the horizon ...).

One of the screens contains a scrollable list of items. I would like the list to behave similarly to the built-in iOS Mail application.

In other words...

  • If the user touches the list and moves up or down, the list scrolls vertically.
  • If the user clicks up or down, the list scrolls vertically with a natural momentum
  • If the user touches the list and moves ONLY to the left - a specific element moves to the left, opening the delete button.
  • IMPORTANT - the list should scroll, OR , the item should slide , BUT NEVER ANYTHING .

So, it’s important to find out what the user's intention is, which means that I probably need to prevent ANY response until I find out if the user is moving the finger vertically or horizontally.

Just setting these CSS styles into a list container ...

overflow-y: auto; -webkit-overflow-scrolling: touch; 

... I get # 1 and # 2 above. So, I need to figure out how to implement # 3.

My first thought was to implement something like this (pseudo code) ...

  • Create a touchstart event touchstart in the list container. In the callback, save the x- and y-coordinates of the custom touch start position.
  • Create a touchmove event touchmove in the list container. In the callback, find out how far the user's finger moves (e.g. delta_x and delta_y)
  • If delta_x AND delta_y - less than 10 pixels - do nothing (do not scroll through the list or move the item) - since we have not yet figured out whether the user plans to move up / down or left / right.
  • If EITHER delta_x OR delta_y is more than 10 pixels, we can assume that the user has moved far enough to express his intention. If delta_y> delta_x, suppose it moves up / down and allows you to scroll through the list, but does not move the item. If delta_x> delta_y, suppose it moves left / right, so we must allow the item to slide, but not allow scrolling of the list.

I expected that I would use event.preventDefault () in touchstart or touchmove to control when scrolling starts. For example.

 div.addEventListener("touchstart", function(e) { touchStart = { x: e.touches[0].pageX, y: e.touches[0].pageY } }, false); div.addEventListener("touchmove", function(e) { touchNow = { x: e.touches[0].pageX, y: e.touches[0].pageY } var dx = touchStart.x - touchNow.x, dy = touchStart.y - touchNow.y; if ((Math.abs(dx) < 10) && (Math.abs(dy) < 10)) { // prevent scrolling e.preventDefault(); } else if (Math.abs(dx) > Math.abs(dy) < 10) { // moving right/left - slide item } else { // moving up/down - allow scrolling } }, false); 

However - this does not work. No matter how far you move, the list NEVER scrolls.

Obviously - I don't understand what triggers the scroll, and what the .preventDefault () event should do in this context.

So - is there a way to accomplish what I need?

I am hoping for a clean JavaScript solution (so I understand it better), but the jQuery approach will be fine. I definitely hope to avoid the jQuery plugin / library, if at all possible ...

Thanks in advance!

+7
javascript jquery ios web-applications webkit
source share
3 answers

I do not propose to reinvent the wheel. There are many libraries that support gesture detection. From this, I suggest using Hammer.js to detect touch events.

It has no dependencies, and it is small, only 3.96 kB minified + gzipped!

And it's all about handling touch events, nothing more.

In your case, Hammer has built-in swipe detection.

You can configure the default tab gesture by specifying:

  • direction: the direction in which you want to detect with a swipe gesture ( more )
  • threshold: minimum distance to recognition
  • speed: minimum speed necessary before recognition (unit is px / ms)

    and more.

Below is a simple example (Stack Snippet seems to have a problem with emulating touch events, the fiddle works fine):

 var myElement = document.getElementById("container"); var hammertime = new Hammer(myElement, {}); hammertime.get('swipe').set({ direction: 2 // 2 stands for left }) hammertime.on('swipe', function(event) { event.target.querySelector(".delete").classList.add("show"); }); 
 * { margin: 0; padding: 0; } #container { width: 250px; height: 300px; overflow-y: auto; -webkit-overflow-scrolling: touch; background: dodgerblue; } #list { height: 100%; list-style: none; } #list li { position: relative; box-sizing: border-box; width: 100%; height: 50px; border: 2px solid #fff; } #list li span.delete { display: inline-block; position: absolute; left: 250px; width: 50px; height: 40px; line-height: 40px; margin: 3px; text-align: center; background: #fff; transition: left 0.5s ease-in; } #list li span.delete.show { left: 170px; } 
 <script src="http://cdn.jsdelivr.net/hammerjs/2.0.4/hammer.min.js"></script> <div id="container"> <ul id="list"> <li class="item"><span class="delete">&#215;</span> </li> <li class="item"><span class="delete">&#215;</span> </li> <li class="item"><span class="delete">&#215;</span> </li> <li class="item"><span class="delete">&#215;</span> </li> <li class="item"><span class="delete">&#215;</span> </li> <li class="item"><span class="delete">&#215;</span> </li> <li class="item"><span class="delete">&#215;</span> </li> <li class="item"><span class="delete">&#215;</span> </li> <li class="item"><span class="delete">&#215;</span> </li> <li class="item"><span class="delete">&#215;</span> </li> </ul> </div> 

If you are more interested in learning how the touch event works, rather than doing the work, I suggest looking under the hood of Hammer.


There is a little Hammer.js jQuery plugin , for those who cannot part with jQuery.

+3
source share

I had a similar problem when I implemented a slide show. This is the solution that worked for me, hope it helps you:

 function touchStart(event){ if (!event) event = window.event; if(!touched){ touched = true; var touchObj = event.changedTouches[0]; touchXPos = parseInt(touchObj.clientX); touchYPos = parseInt(touchObj.clientY); } return false; } 

When the user touches the screen, cooridnate positions are saved in touchXPos, touchYPos, and the boolean variable "touched" is set to true.

 function touchMove(event){ if (!event) event = window.event; if(touched){ var touchObj = event.changedTouches[0]; var distanceX = touchXPos - parseInt(touchObj.clientX); var distanceY = touchYPos - parseInt(touchObj.clientY); if(!touchDirection) { if(Math.abs(distanceX) > Math.abs(distanceY)){ if (distanceX > 0) touchDirection = "right"; else if (distanceX < 0) touchDirection = "left"; } else{ if (distanceY > 0) { touchDirection = "up"; } else if (distanceY < 0) {touchDirection = "down"; } } } if (((touchDirection == "right") )|| ((touchDirection == "left"))){ //update touchDirection if(Math.abs(distanceX) > Math.abs(distanceY)){ if (distanceX > 0) touchDirection = "right"; else if (distanceX < 0) touchDirection = "left"; } touchXPos = parseInt(touchObj.clientX); slideshow_mask.scrollLeft += distanceX; } else if (((touchDirection == "up") ) || ((touchDirection == "down") )){ return false; } event.preventDefault(); return false; } 

Like your approach, I define delta-x and delta-y whenever a touch occurs, which I called distanceX and distanceY. When this is the first "move", I define a touch that can be right, left, up or down. The next movement that is recorded can only remain within this initial time, i.e. Up / Down OR Left / Right. Thus, if the initial direction was, for example, β€œright”, the user can slide horizontally (left or right), but never up / down.

Since my code was designed to move the slide show horizontally, I was just interested in horizontal movement (-> in the code fragment, which you can see that I updated the direction (left, right), saved the new xPosition and moved the slide show in this direction), but it’s not too difficult to adapt it to vertical scrolling.

+1
source share

I had the same task and I wrote pure Javascript lib swiped.js for horizontal scrolling in a list

+1
source share

All Articles