Range slider in slide box slide (how to prevent event bubbling)


#1

I’m trying to use a list of range inputs on a slide of a slide box. For this to work, I need to stop the drag events on the range input slider to bubble up to the slide. Otherwise, while moving the slider knob, the slide itself starts to move.

This should be really simple (in theory) but I’m new to angular and I’m not sure how to tackle this problem. I created a directive targeting input elements with type=“range” and essentially add a drag handler to override the default.

Here’s what I have so far:

.directive('input', function ($ionicGesture) {
  return {
    restrict: 'E',
    link: function ($scope, e, attrs) {
      if(attrs.type =="range") {
        var dragHandler = function(e) {
            e.gesture.stopPropagation();
            e.gesture.preventDefault();
            e.stopPropagation();
            e.preventDefault();
        }
        var onDrag = $ionicGesture.on('drag', dragHandler, e);
      }
    }
  }
})

However, this doesn’t change anything. Now I think that my approach is essentially wrong, because I’m adding a new handler instead of adding to an existing one. But I can’t see the point where I can “hook” myself in to reach the actual event which is responsible for handling the range input.

I would be very happy for a little guidance on this topic. :slight_smile:


#2

Hmm, let me try to take a crack at this, see what I can do. I’ll get back to you


#3

It’s not perfect but it should work for your case.

You were in the right direction by creating the directive and figuring you need to target the slide.

.directive('preventDrag', function($ionicGesture, $ionicSlideBoxDelegate) {
  return {
    restrict :  'A',

    link : function(scope, elem, attrs, e) {
      var reportEvent = function (e){

        if  (e.target.tagName.toLowerCase() == 'input'){

         // No need for e.prevent bullshit, 
        // we got a built-in for that :)
          $ionicSlideBoxDelegate.enableSlide(false);

        }
      };


      $ionicGesture.on('drag', reportEvent, elem);
    }
  };
});

#4

Thanks @mhartington - this works nicely. It’s smarter to implement a “spam filter” on the level of the element that shouldn’t be affected by the bubbling than to try to stop bubbles from being emitted.
One thing I noticed is that the first time “drag” happens, the underlying slide still moves. But not the second time. Will try different events (dragstart, …) to see if it helps.


#5

Hmm, I’m noticing that too. I can open up an issue for this.


#6

I was playing around a bit with this particular case. Once the slide is disabled, it is … well, disabled. I wasn’t able to bring it back unless I attach “drag end” and then enabling the slide again. Problem is, the “first time it doesn’t work” becomes a “every time is a first time”. I then rewrote the entire thing to use “dragstart” and “dragend”, but now it feels completely wonky. :wink:

IMHO elements like slides and scrolls should provide a way to immediately ignore a touch event from a child if told so. I was trying to get there using stopPropagate(); but I couldn’t make it work. This seems to me more like voodoo than anything else. :stuck_out_tongue:

Eventually, I had to stop event propagation directly inside of a plugins touch routine (jqKnob). But it would be way easier to simply tell a parent element (slide in this example) to ignore or filter a certain event emitted by a certain child. Seems like the cleanest solution to me.


#7

I realize this post is a little old, but I’ve just been fighting with this same issue for a while now and found this post. I was just about to ask for any updates when I figured out a working solution so I thought I’d share it.

Instead of binding on dragstart/dragend I’m binding on touchstart/touchend, and to have it working in browsers I’m of course binding mouseover/mouseleave as well. Works like a charm!

Hope this helps someone else :slight_smile:


#8

Listening for ‘touch’ instead of ‘drag’ actually gives a cleaner result. ‘drag’ is fired after the slide already moved a few pixels.

My code:

  .directive('preventDrag', function ($ionicGesture, $ionicSlideBoxDelegate) {
    return {
      restrict: 'A',
      link    : function (scope, elem) {
        var reportEvent = function (e) {
          if (e.target.tagName.toLowerCase() === 'input') {
            $ionicSlideBoxDelegate.enableSlide(false);
          } else {
            $ionicSlideBoxDelegate.enableSlide(true);
          }
        };
        $ionicGesture.on('touch', reportEvent, elem);
      }
    };