Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removing listeners can cause an exception depending on how you call off() #518

Closed
githubdorey opened this issue Aug 12, 2016 · 3 comments
Closed

Comments

@githubdorey
Copy link

Line 3277 (version 2.3.4) can cause an exception when removing listeners. If you do an off() and then off("name.listener") can cause exceptions.

I use several plugins like resize.js, draggable.js. Each have their own clean up function that removes listeners.

Example of my code. var shapes= draw.set()

function cleanupExtensions() {
    shapes.selectize(false, {deepSelect:true});
    shapes.each(function(i) {
      this.resize('stop');
      this.draggable(false);
    });
}

...
shapes.off(); // cleanup my own listeners
cleanupExtensions();

By invoking shapes.off() i believe
delete SVG.listeners[index]
is called and when this.resize('stop'); and this.draggable(false); are called then
if (SVG.listeners[index][ev] && SVG.listeners[index][ev][ns]) {
will cause an exception. Because they are doing individual off() calls. Like this.el.off("lt.resize");

My fix is to add a null check in the if statement.

if (SVG.listeners[index] && SVG.listeners[index][ev] && SVG.listeners[index][ev][ns]) {

SVG.off = function(node, event, listener) {
  var index = SVG.handlerMap.indexOf(node)
    , ev    = event && event.split('.')[0]
    , ns    = event && event.split('.')[1]

  if(index == -1) return

  if (listener) {
    if(typeof listener == 'function') listener = listener._svgjsListenerId
    if(!listener) return

    // remove listener reference
    if (SVG.listeners[index][ev] && SVG.listeners[index][ev][ns || '*']) {
      // remove listener
      node.removeEventListener(ev, SVG.listeners[index][ev][ns || '*'][listener], false)

      delete SVG.listeners[index][ev][ns || '*'][listener]
    }

  } else if (ns && ev) {
    // remove all listeners for a namespaced event
    if (SVG.listeners[index][ev] && SVG.listeners[index][ev][ns]) {  // THIS IS WHERE THE EXCEPTION THROWN
      for (listener in SVG.listeners[index][ev][ns])
        SVG.off(node, [ev, ns].join('.'), listener)

      delete SVG.listeners[index][ev][ns]
    }

  } else if (ns){
    // remove all listeners for a specific namespace
    for(event in SVG.listeners[index]){
        for(namespace in SVG.listeners[index][event]){
            if(ns === namespace){
                SVG.off(node, [event, ns].join('.'))
            }
        }
    }

  } else if (ev) {
    // remove all listeners for the event
    if (SVG.listeners[index][ev]) {
      for (namespace in SVG.listeners[index][ev])
        SVG.off(node, [ev, namespace].join('.'))

      delete SVG.listeners[index][ev]
    }

  } else {
    // remove all listeners on a given node
    for (event in SVG.listeners[index])
      SVG.off(node, event)

    delete SVG.listeners[index]

  }
}
@Fuzzyma
Copy link
Member

Fuzzyma commented Aug 12, 2016

Good catch! Can you pls create a testcase which shows that error? That would help a lot and speed up the fixing process.

However: You should avoid calling a general off() on an element. You could end up removing listeners which arent yours which then would lead to wrong behavior.
Not every plugin will gracefully accept your interference as svg.select or svg.resize do.
So you better get used to using a namespace for your own bindings, too.

@githubdorey
Copy link
Author

svg-test-off-exception.zip

There is just one file. It is not a Jasmine test case but just an html file that shows the error.

The off() works for me in this case because I have a controlled Set of objects that I apply it to, so I can be lazy and just call off() And having no listeners on those objects is exactly what I want.

@Fuzzyma
Copy link
Member

Fuzzyma commented Aug 12, 2016

The intention of this test is to have our code better tested with higher code coverage and that this bug is not reintroduced later. So a jasmine test is what I was aiming for :D

@Fuzzyma Fuzzyma closed this as completed Mar 5, 2017
Fuzzyma added a commit that referenced this issue Mar 10, 2017
### Added
- added a plot and array method to `SVG.TextPath` (#582)
- added `clone()` method to `SVG.Array/PointArray/PathArray` (#590)
- added `font()` method to `SVG.Tspan`
- added `SVG.Box()`
- added `transform()` method to boxes
- added `event()` to `SVG.Element` to retrieve the event that was fired last on the element (#550)

### Changed
- changed CHANGELOG to follow the conventions described in [“Keep a CHANGELOG”](http://keepachangelog.com) (#578)
- make the method plot a getter when no parameter is passed for `SVG.Polyline`,`SVG.Polygon`, `SVG.Line`, `SVG.Path` (related #547)
- allow `SVG.PointArray` to be passed flat array
- change the regexp `SVG.PointArray` use to parse string to allow more flexibility in the way spaces and commas can be used
- allow `plot` to be called with 4 parameters when animating an `SVG.Line`
- relative value for `SVG.Number` are now calculated in its `morph` method (related #547)
- clean up the implementation of the `initAnimation` method of the FX module (#547, #552, #584)
- deprecated `.tbox()`. `.tbox()` now map to `.rbox()`. If you are using `.tbox()`, you can substitute it with `.rbox()` (#594, #602)
- all boxes now accept 4 values or an object on creation
- `el.rbox()` now always returns the right boxes in screen coordinates and has an additional paramater to transform the box into other coordinate systems
- `font()` method can now be used like `attr()` method (#620)
- events are now cancelable by default (#550)

### Fixed
- fixed a bug in the plain morphing part of `SVG.MorphObj` that is in the FX module
- fixed bug which produces an error when removing an event from a node which was formerly removed with a global `off()` (#518)
- fixed a bug in `size()` for poly elements when their height/width is zero (#505)
- viewbox now also accepts strings and arrays as constructor arguments
- `SVG.Array` now accepts a comma seperated string and returns array of numbers instead of strings
- `SVG.Matrix` now accepts an array as input
- `SVG.Element.matrix()` now accepts also 6 values
- `dx()/dy()` now accepts percentage values, too but only if the value on the element is already percentage
- `flip()` now flips on both axis when no parameter is passed
- fixed bug with `documentElement.contains()` in IE
- fixed offset produced by svg parser (#553)
- fixed a bug with clone which didnt copy over dom data (#621)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants