简体   繁体   中英

Prevent hover on an element when scrolling event happened

I've built a dropdown widget using backbone/marionette that let you use keyboard to navigate up and down the dropdown list. I need to make the distinction between selected and highlighted so I am storing the boolean value in the list item model's isSelected and isHighlighted. Whenever the item is hovered, the isHighlighted is changed to true.

Here is my problem, I have setup the dropdown list to have a maxItem, so when there's too many items in the dropdown, I am showing a scrollbar so people can scroll to see more list items. When I use the keyboard navigation to navigate pass that maxItem limit, I call for an animate and scrollTop a certain distance so that the highlighted stay in view. The problem is if I happen to have the mouse within the dropdown list and this scrolling happened, it will trigger a mouseenter event on the item that is at where the mouse pointer is. Then it will change the hovered element's isHighlighted to true. That means the highlighted item went back up and when I use keyboard to navigate down again, it will go down from top instead of continue from where I was before.

Is there anyway I can prevent the mouseenter event when the scroll event fires?

Trying to include all the relevant code, hope this helps:

onKeyUp: function (event) {
    var key = event.which;

    switch (key) {
        case 38: // UP
            this.onUpArrow();
            break;
        case 40: // DOWN
            this.onDownArrow();
            break;
        default:
            // default method
    }
},
onDownArrow: function () {
    if (!this.isDropdownOpen()) {
        this.openDropdown();
    } else {
        this.dropdown.highlightNext();
        this.dropdown.scroll();
    }
},

In the other file, I have

events: {
    'mouseenter': 'onMouseEnter'
},
onMouseEnter: function (event) {
    this.triggerMethod('dropdown:option:mouseenter', {
        event: event,
        view: this,
        model: this.model
    });
},

In yet another file, I have

onDropdownOptionMouseenter: function () {
    var data = _.last(arguments),
        event = data.event,
        view = data.view;
    view.highlight();
}

At this point, the scrolling happened, and mouseenter trigger has been fired and the highlighting changed. I just don't know where I can add the code to prevent the browser to recognize the mouseenter. Am I suppose to add custom events for this? That is a lot of work imo, there has to be an easier solution.

Setting up an event aggregator

Making an event aggregator in Backbone is trivial, just extend the events object,

window.vent = _.extend({}, Backbone.Events);

We attach the event aggregator, vent , to the global scope so that anyone can listen in.

If you're using Marionette you can use the built-in aggregator in Backbone.Wreqr the built-in Marionette messaging system ( Note: Wreqr will be switched out with a new messaging API, Radio , starting with Marionette v.3, but I have it from a good source that the update manager should migrate all Wreqr instances to Radio ).

Createing an event aggregator is a matter of calling,

window.vent = new Backbone.Wreqr.EventAggregator();

Now to help your problem, we'll use the event aggregator to set up a pubsub system, where your item's view will subscribe to an event that will help them to properly react to a scroll. The view that manages the scroll will be publishing this event.

Note: Backbone.Wreqr can also be equipped to use Channels if you want to create independent messaging pipelines. Check out the docs .

Subscribing

So let's set up the subscriptions. In the item's view do

initialize: function ()  {
  this.listenTo(vent, "dropdown:before:scroll", function() {
    this.isScrolling = true;
  }
  this.listenTo(vent, "dropdown:end:scroll", function() {
    this.isScrolling = false;
  }
}

Publishing

The events we just subscribed to will be published right before and after the scrollTop call, by triggering the event on vent . So you'd rewrite the maxlimit checker like this,

if (currItem > maxItem) {
  vent.trigger("dropdown:before:scroll");
  someElement.scrollTop();
  vent.trigger("dropdown:end:scroll");
}

The items listening to these events will know that you're scrolling since trigger events are synchronous and will fire in the correct order.

Preventing the highlight

Finally, now that you're publishing the scrollTop event and your items are listening for it, you make sure that the item that encounters a mouseenter knows when not to highlight.

onMouseEnter: function (event) {
  if (!this.isScrolling) {
    this.triggerMethod('dropdown:option:mouseenter', {
        event: event,
        view: this,
        model: this.model
    });
  }
},

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM