Robs
Robs

Reputation: 8279

How to remove JavaScript touch event handler from document and window?

I have a web page which displays a calendar by FullCalendar

The calendar is being hosted inside a public Salesforce Community page.

I want to be able to create events in the calendar via a mobile device using touch and drag.

Both Salesforce Communities and FullCalendar support mobile devices.

And FullCalendar has touch support

But when I access the community page using a mobile device I am unable to use the touch and drag to create a calendar entry.

There are touch event handlers created by the Salesforce community page/framework:

Which seem to override the FullCalendar touch events.

Here is an example calendar for debugging:

How can I remove the touch event handlers on the document and window?

I've tried adding:

    document.addEventListener("touchstart", function (event) {
        event.preventDefault();
        console.log("touchstart: event.preventDefault()");
    }, false);

    document.addEventListener("touchend", function (event) {
        event.preventDefault();
        console.log("touchend: event.preventDefault()");
    }, false);

    document.addEventListener("touchcancel", function (event) {
        event.preventDefault();
        console.log("touchcancel: event.preventDefault()");
    }, false);

    window.addEventListener("touchmove", function (event) {
        event.preventDefault();
        console.log("touchmove: event.preventDefault()");
    }, false);

But I just got this error in the console:

[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.

And none of the calendar buttons worked anymore.

Upvotes: 2

Views: 4583

Answers (4)

Mike Sowerbutts
Mike Sowerbutts

Reputation: 35

Maybe try forwarding the "SFDC" touch event to the FullCalendar event

document.addEventListener("touchstart", function (event) {
    myFullCalendarEvent.OnTouch(event);
}, false);

Though generally, I have never tried to remove events like this when I still expect it to work at a lower level... Given you are having problems with your current approach, this is where I would go next..

Upvotes: 0

Old Pro
Old Pro

Reputation: 25547

Not sure exactly what problem you (the OP) are having. https://sfse-developer-edition.eu16.force.com/FullCalendar/s/ mostly works for me when viewed on an iPad running Safari. I found some bugs (see below), but maybe you are just not touching long enough.

According to the FullCalendar documentation: "On a touch device, for the user to begin drag-n-dropping events, they must first tap-and-hold on the event in order to “select” it!" (emphasis in original). You can configure the delay using longPressDelay.

Whatever problem you are having, I'd say it's highly unlikely that the best fix involves removing event handlers. More likely you will find the answer in the documentation for SalesForce Community about how to integrate other JavaScript packages into a page.

For example, I found a bug in this bit of code, which Chrome says is from https://sfse-developer-edition.eu16.force.com/FullCalendar/s/components/c/FullCalendar.js but does not actually seem to come from that URL. Anyway, given the console.log('loadCalendar') line, I'm guessing you, the OP, wrote it.

 "helper":{
    "loadCalendar":function(component) {
        console.log('loadCalendar');
        var params;
        var self = this;
        var calendar = component.find('calendar').getElement();
        $(calendar).fullCalendar('destroy');
        $(calendar).fullCalendar({
            header: {
                left: 'prev,next',
                center: 'title',
                right: 'month,agendaWeek,agendaDay'
            },
            schedulerLicenseKey: '0537034756-fcs-1530699190',
            defaultView: 'agendaWeek',
            // <snip> bunch of stuff omitted for brevity
            select: function (starts, ends) {
                params.starts = starts.format('x');
                params.ends = ends.format('x');

            },
            // <snip> lots more stuff omitted

The problem here is that params is never initialized, so params.starts is an invalid reference that throws an uncaught TypeError. This can probably be solved by replacing var params; with var params = {}; but I can't be sure.

Upvotes: 1

Rubin bhandari
Rubin bhandari

Reputation: 1951

The EventTarget.removeEventListener() method removes from the EventTarget an event listener previously registered with EventTarget.addEventListener(). The event listener to be removed is identified using a combination of the event type, the event listener function itself, and various optional options that may affect the matching process; see Matching event listeners for removal.

More on https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener

Upvotes: 0

Bharata
Bharata

Reputation: 14155

You can not remove the listener if you add new listener. All listeners will be added in the queue. You can see it for example here:

var listenerLoad = function(e){console.log(1)};
window.addEventListener('load', listenerLoad, false);
window.addEventListener('load', listenerLoad, true);
window.addEventListener('load', function(e){console.log(3)}, false);
window.onload = function(e){console.log(4)};

But you can remove the listener if you use the appropriate function for this. You have to use the function EventTarget.removeEventListener(). Read this documentation carefully before. The parameters have to be the same like in addEventListener() by initialization from this events. And the pointers to the listerners should be the same like in addEventListener(). For example if you they have the pointers to the listerners like:

var listenerTouchStart = function(e){/**/},
    listenerTouchEnd = function(e){/**/},
    listenerTouchCancel = function(e){/**/},
    listenerTouchMove = function(e){/**/};

then you can delete the listeners like follows:

document.removeEventListener('touchstart', listenerTouchStart, false);
document.removeEventListener('touchend', listenerTouchEnd, false);
document.removeEventListener('touchcancel', listenerTouchCancel, false);
window.removeEventListener('touchmove', listenerTouchMove, false);

But do not forget that window.removeEventListener('touchmove', listenerTouchMove, false); and window.removeEventListener('touchmove', listenerTouchMove, true); are not the same. If they add the listener with false and you try to remove it with true then it will not work. Be careful!

If you want to find the listener function names then you have 3 ways:

  1. You can search manually in the code.
  2. In the developer console in Chrome DevTools(Opera inclusive), Safari Inspector and in Firebug you can type in console getEventListeners(Object);. For example for our window in the first example you will have an output like this:

enter image description here

And if the listener was added with anonymous function then it does not have a name.

  1. You can use ListenerTracker script. Here is the source code of it (do not try to execute this snippet – it is only to hide this long code):

// THIS SNIPPET SHOULD NOTHING DO.
// IT IS ONLY TO HIDE THIS LONG CODE

function DO_NOT_COPY_THIS_LINE() //<-DO NOT COPY THIS LINE
{
    //////////////////////////////
    //ListenerTracker Script START
    //////////////////////////////

    var ListenerTracker = new function()
    {
        var is_active=false;
        // listener tracking datas
        var _elements_  =[];
        var _listeners_ =[];
        this.init=function(){
            if(!is_active){//avoid duplicate call
                intercep_events_listeners();
            }
            is_active=true;
        };
        // register individual element an returns its corresponding listeners
        var register_element = function(element){
            if(_elements_.indexOf(element)==-1){
                // NB : split by useCapture to make listener easier to find when removing
                var elt_listeners=[{/*useCapture=false*/},{/*useCapture=true*/}];
                _elements_.push(element);
                _listeners_.push(elt_listeners);
            }
            return _listeners_[_elements_.indexOf(element)];
        };
        var intercep_events_listeners = function(){
            // backup overrided methods
            var _super_={
                "addEventListener"      : HTMLElement.prototype.addEventListener,
                "removeEventListener"   : HTMLElement.prototype.removeEventListener
            };

            Element.prototype["addEventListener"] = function(type, listener, useCapture){
                var listeners=register_element(this);
                // add event before to avoid registering if an error is thrown
                _super_["addEventListener"].apply(this,arguments);
                // adapt to 'elt_listeners' index
                useCapture=useCapture?1:0;

                if(!listeners[useCapture][type])listeners[useCapture][type]=[];
                listeners[useCapture][type].push(listener);
            };

            Element.prototype["removeEventListener"] = function(type, listener, useCapture){
                var listeners=register_element(this);
                // add event before to avoid registering if an error is thrown
                _super_["removeEventListener"].apply(this,arguments);
                // adapt to 'elt_listeners' index
                useCapture=useCapture?1:0;
                if(!listeners[useCapture][type])return;
                var lid = listeners[useCapture][type].indexOf(listener);
                if(lid>-1)listeners[useCapture][type].splice(lid,1);
            };

            Element.prototype["getEventListeners"] = function(type){
                var listeners=register_element(this);
                // convert to listener datas list
                var result=[];
                for(var useCapture=0,list;list=listeners[useCapture];useCapture++){
                    if(typeof(type)=="string"){// filtered by type
                        if(list[type]){
                            for(var id in list[type]){
                                result.push({"type":type,"listener":list[type][id],"useCapture":!!useCapture});
                            }
                        }
                    }else{// all
                        for(var _type in list){
                            for(var id in list[_type]){
                                result.push({"type":_type,"listener":list[_type][id],"useCapture":!!useCapture});
                            }
                        }
                    }
                }
                return result;
            };
        };
    }();
    ListenerTracker.init();

    //////////////////////////////
    //ListenerTracker Script END
    //////////////////////////////
}

I have found this ListenerTracker script here. With this script you will get even anonymous listeners, but you have to add it before they add some listeners in the code.

Good luck!

Upvotes: 9

Related Questions