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

Exception removing event listener #905

Closed
jshailes opened this issue Sep 18, 2018 · 16 comments
Closed

Exception removing event listener #905

jshailes opened this issue Sep 18, 2018 · 16 comments

Comments

@jshailes
Copy link

jshailes commented Sep 18, 2018

I think I've followed the documentation when trying to remove a click event listener (http://svgjs.com/events/#element-click) however it results in an exception (I'm using 2.6.3):

Uncaught TypeError: Cannot read property 'bind' of null
    at Function.SVG.on (svg.js:3417)
    at initializer.SVG.Element.(/impact/anonymous function) [as click] (https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.3/svg.js:3404:9)

Code is as follows:

var draw = SVG('drawing').size(300, 130);
var rect = draw.rect(100, 100);
rect.click(null);

It might be worth noting that the error also occurs when removing the listener in the more likely case where a click listener has previously been set:

var draw = SVG('drawing').size(300, 130);
var rect = draw.rect(100, 100);
rect.click(function() { alert("Clicked" });
rect.click(null);
@Fuzzyma
Copy link
Member

Fuzzyma commented Sep 18, 2018

I think the documentation is not up to date with this one (or the code isnt - it depends^^).
For the meantime just use the slightly longer verion: rect.off('click')

@jshailes
Copy link
Author

Great, thanks very much

@jshailes
Copy link
Author

I just tried this but it seems that a reference to the object is still held in memory after 'removing' the listener. I'm reasonably sure the listener is the issue as the rectangle object disappears from memory if I don't add a listener to it.

Any suggestions?

@Fuzzyma
Copy link
Member

Fuzzyma commented Sep 18, 2018

Hm - all listener are saved on the element itself. We fixed this memory leak a while ago (#805 #807). Are you sure you are using the latest release?

@jshailes
Copy link
Author

Am I right in thinking that went in to 2.6.5? I'm currently running 2.6.3.

@Fuzzyma
Copy link
Member

Fuzzyma commented Sep 19, 2018

It's time to upgrade then :D. You can take a look into the changelog file and double check which version it is was added and what changed overall. It should not cause any issues to update a minor version

@jshailes
Copy link
Author

I've updated to version 2.6.6 and it seems the issue remains. Below is the code I've been testing with:

<html>
	<head>
		<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.6/svg.js"></script>
		<script   src="https://code.jquery.com/jquery-3.2.1.js"   integrity="sha256-DZAnKJ/6XZ9si04Hgrsxu/8s717jcIzLy3oi35EouyE="   crossorigin="anonymous"></script>
	</head>
	<body>
		<div id="drawing"></div>
		<button id="addEventListeners">Add Event Listeners</button>
		<button id="removeEventListeners">Remove Event Listeners</button>
		<button id="removeElements">Remove Elements</button>

		<script type="text/javascript">
			//create svg drawing
			var draw = SVG('drawing').size(300, 130);
			
			//create new elements in the drawing
			var svgElements = [];
			for(var i=0; i<20; i++) {
				var svgElement = draw.circle(10, 10);
				svgElements.push(svgElement);
			}

			// add click event listener to each element
			$('#addEventListeners').click(function() {
				for(var i=0; i<svgElements.length; i++) {
					var svgElement = svgElements[i];
					svgElement.click(function(){
						alert("Hi SVG Element");
					});
				}
			});

			// remove click event listener to each element
			$('#removeEventListeners').click(function() {
				for(var i=0; i<svgElements.length; i++) {
					var svgElement = svgElements[i];
					svgElement.off("click");
				}
			});

			// remove elements including the drawing
			$('#removeElements').click(function() {
				for(var i=0; i<svgElements.length; i++) {
					var svgElement = svgElements[i];
					svgElement.remove();
					svgElement = null;
				}
				svgElements = [];

				draw.clear();
				draw.remove();
				draw = null;
			});
		</script>
	</body>
</html>

I've taken memory dumps in the following scenarios:

  1. Load page
  2. Load page, Remove Elements
  3. Load page, Add Event Listeners, Remove Event Listeners, Remove Elements

In Scenario 1, 23 circle elements exist and none are detached. I'm not sure what the extra 3 are but it seems consistent amongst scenarios so I've ignore these. This is the behaviour I expect.

In Scenario 2, 3 circle elements exist and none are detached. This is the behaviour I expect.

In Scenario 3, 3 circle elements exist and 20 are detached (but still reachable from the window). I would have expected 3 to exist and none to be detached (as in scenario 2 where listeners hadn't been created).

@Fuzzyma
Copy link
Member

Fuzzyma commented Sep 20, 2018

What do you mean with:

(but still reachable from the window).

@jshailes
Copy link
Author

That's the description the dev tools in Google Chrome gives. Detached means that it's no longer in the dom, and since it still appears in the memory dump I assume 'but still reachable from the window' means that it's still held in memory for the window.

@Fuzzyma
Copy link
Member

Fuzzyma commented Sep 20, 2018

can it also give you the path from where it is reachable? That would help a lot!

@jshailes
Copy link
Author

It does - when you take the memory dump, select one of the detached circle instances and it lists the retainers at the bottom. I tried to fathom it out but it didn't make sense to me.

handlerMap in ()     -   svg.js:29 
  - SVG in Window

@Fuzzyma
Copy link
Member

Fuzzyma commented Sep 20, 2018

Thank you!

@jshailes
Copy link
Author

Just having a look at it again. In the SVG.off method, where !ns && !ev there's a reference to deleting items from the handlerMap. I've not yet understood how it works but I see no reference to deleting from handlerMap in !ns && ev where the code I posted enters. I tried adding delete SVG.handlerMap[index] in there and it made most of the detached circles get removed from memory but left 1 instance for some reason.

@Fuzzyma
Copy link
Member

Fuzzyma commented Sep 20, 2018

oooohh right! We fixed this in the 3.0 and forgot to backport it! So yes you are completely right. There is a memory leak. Take a look into the 3.0 branch in the event.js file were you can see what was changed to fix this issue. I am currently working to get 3.0 out as soon as possible so I am short on time. Do you think you can create a PR for this?

@jshailes
Copy link
Author

I'm sorry but from looking at the master vs 3.0.0 branch I don't understand what changes need to be made. No worries about getting it fixed in 2.x, I can wait until 3.0 is out.

Fuzzyma added a commit that referenced this issue Nov 13, 2018
### Fixed
- fixed calling `parent()` on `documentFragment`s children (#927)
- parser is not focusable anymore (#908)
- `SVG.Element.click(null)` correctly unbinds the event (#878)
- fix memory leak (#905)

### Added
- `SVG.Set` now accepts another Set as input (#893)
- `on()/off()` accepts multiple event names as input (backport from 3.0)
@Fuzzyma
Copy link
Member

Fuzzyma commented Nov 13, 2018

I backported the fix so that shouldnt be an issue anymore. See 2.7.0

@Fuzzyma Fuzzyma closed this as completed Nov 13, 2018
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