bootstrap affix plugin memory leak - javascript

These lines in the bootstrap affix plugin seem to cause a memory leak because the window gets a reference to the affix instance that's never released.
As a workaround I'm using this code to release the references when removing the affixed element from the DOM:
$(window)
.off('scroll.bs.affix.data-api')
.off('click.bs.affix.data-api');
Seems kind of hacky- is there a better way of doing this? Didn't see anything in the affix plugin docs.

By default, bootstrap Affix listens for scroll and click events on $(window) using the .bs, .affix, and .data-api namespaces.
$.off('.affix'); will remove all listeners in the .affix namespace.
$(window).off('.affix'); will remove all listeners in the .affix namespace from the window element. If you only have one Affix, and are affixing it to the window, it has the exact same effect as $.off('.affix');
Adding in the other namespaces makes it more specific, but unless you are using the .affix namespace in your own code, the added specificity doesn't change anything. You don't want to remove the other namespaces independently of .affix if you are using any other bootstrap elements.
$('.affix').off('.affix'); will not work because the listeners are not on the Affixed element, but on the target that element is Affixed to, i.e. the window.
pstenstrm is correct that there is no way to detect that an element is removed from the DOM, or injected for that matter. So if the code later re-injects the element, and you want to behave as an Affix again, you'll need to use the bootstrap JS api to call Affix again.

I took #Carrie Kendall's recommendation and opened a bug report... well commented on a related bug report.
https://github.com/twbs/bootstrap/issues/13655
What we need in this case is a "destroy" method for the affix plugin and some documentation on the getbootstrap site so that people using the affix plugin in single page apps can avoid the memory leak pitfall when removing their affixed content.

There is no way to detect when an element is removed from the DOM. The affix plugin can't automatically remove the listeners. The way you do it is the right way.
Though by calling $(window).off() you remove every listener, even those you might want to keep. It would be safer to only call $.off() on the element you are removing.
$('.affix').off('.affix');
The .bs, .affix and .data-api after the event name are namespaces. By calling $.off('.affix') you remove every event declared in that namespace. Which is probably the only better of doing what you're doing.

Related

How to find what javascript is acting on a DOM element on page load

I have red other answers however none of them cover my case.
I have a page (http://www.lacertussoftware.com/) and there is some javascript in one of my included files that is setting the min height and height of my page on the body tag. How can i figure out what javascript is doing this? I have 7 or so files that if i remove my parallax effect / nice scrolling / the gap all go away and don't know what is doing it. Breakpointing is not useful as its on page load (especially because the code i have included on the page is not minified.)
Have you considered simply searching the sources for /min-height/ or /body.{0,50}min-height/ (and the CSSOM equivalent minHeight)?
Alternatively you can add a getter/setters for the style property to the HTMLElement/Element prototype which logs accesses to the style property and then forwards calls them to the native browser implementation. If that doesn't work you may also have to instrument .setAttribute() since the style property can be modified that way too.
Obviously this has to be done as early as possible in the document.
You could also try chrome's "break on attributes modification" feature in the the elements view of the dev tools. Or the DOMEvent breakpoints under sources.
You could try inserting a debugger; statement as a first thing in the dom ready handler, all js will pause.
Now right click on the element in the source and add a break on -> attributes modifications

Restrict access to 'style' property in JavaScript

I'm starting to develop a small JavaScript library and I want to make styling of HTML elements possible only through my API (because for some reason I need to have full control over styling).
So I want to make style property inaccessible (my API will access it through my style alias - not an ideal solution, but for other libraries like jQuery it will do the trick).
If I write this (inspired by this topic):
var box = document.getElementById('someElementId');
Object.defineProperty(box, 'style', {
get: function() {
throw 'you cant access style property';
}
});
box.style.color = 'red';
it works for box element only.
Is it possible to do this for all (existing and future) elements in Webkit, Firefox, and IE9+?
I've also tried this:
Object.defineProperty(HTMLElement, 'style', {...
but no luck.
Any help would be greatly appreciated!
Edit
As #Teemu suggested I can write HTMLElement.prototype instead of HTMLElement, and it works fine in FF and IE, but not in Chrome. And it looks like a Chrome bug. Sadly...
Edit2 - why do I need it
The main goal of a library I want to develop is to allow writing styles like:
element.setWidth('parent.width / 2 - 10');
In this case element's width should react on each changing of the parent's width.
But since onresize event is available only for window object (this article seems to be obsolete),
the only way I can "listen" modifying .style.width property is to perform my own API for styling.
And I want to restrict (or at least show warning) direct style modifying because it will break the elements' behavior.
From the comments you can tell that restricting access to the style property is probably not such a good route. But I understand from your question that your reason from trying to go this route is that the resize event only fires for the window object.
If you would be able to circumvent the whole restricting issue with a resize event on just any element, than may I suggest you look into Ben Alman's jQuery resize event plugin. I'm not sure whether you want to develop with jQuery, but even if you don't it may be worth it to read that plugin's code. Basically, what it does is make a hashtable of listeners mapped to the element(s) they are listening on and then in a polling loop (with setTimeout or setInterval) check the size of the elements in that map. If it has changed during the interval (250ms by default) it triggers the listeners itself. It works reasonably well. That specific plugin hooks into jQuery's event system, but you could just make your own addResizeEvent function or something to that effect.
Edit: Upon re-reading your question it dawns on me that it looks like you are trying to develop some mechanism to deal with the downsides of the CSS box model, e.g. that when you give an element 5px padding and a width of 50% it will end up being 10 pixels wider than half the parent container. Consider box-sizing: border-box if that is the case.

How do I use the "live" feature of bootstrap's popovers?

In the old days, bootstrap's popover had a "live" option which allowed us to make the $('.myclass').popover({live: true}) call even before the DOM elements existed. I looked at the docs for 2.2 and this seems to be gone. What's the new way to do it?
The live mode is not used any more.
The Bootstrap JS plugins now use delegated events via the jQuery .on method. You can supply an event delegation target selector pointing to an element which might or might not exist.
$('.some-container').popover({
selector: '.has-popover'
});
Working example generating DOM elements with popover attached: http://jsfiddle.net/asKF9/

What can prevent the CSS :hover class from working?

What can prevent the CSS :hover pseudo class from getting triggered, either with CSS or JavaScript?
I'm trying to debug a really tricky problem in a third party's custom calendar control. The client wants to highlight the dates when the user hovers over them--pretty standard stuff. But something is stubbornly preventing the :hover class from working as expected.
I'm able to target the date cells with a selector and manipulate the background-color without any issues. Adding the :hover pseudo class to that selector doesn't work, though.
Unfortunately I can't post any code, because this is a private app that requires an account to use. There's honestly so much spagetti that I wouldn't even know what to include... The control is built in pure JS, no jQuery here, no siree. Several hundred lines of JS, plus hundreds of lines of CSS ... Not sure what's wrong with using the jQuery calendar control.
Is there a known set of things that can break :hover? Could it be an event propagation issue, caused by returning false from an onhover eventhandler or something?
Any other suggestions for debugging this problem? Using the browser tools isn't very effective, because the :hover rules don't get triggered unless I'm hovering. Catch 22 there.
EDIT: This answer seems like a possible culprit. They're definitely using some absolute positioning in the mix...
EDIT 2: Manually applying the :hover state with Chrome's developer tools works, confirming that my selector isn't the issue. Something is preventing the :hover state from being triggered. The calendar control is implemented as an HTML table, with each week in a row and each date in a cell. It does seem like a layering issue, but adding a high z-index to the cells doesn't do anything.
Chrome's "Developer Tools" allows you to toggle an element's state. Perhaps you could try that and see if you gain more insight into the problem you are experiencing?
It tends to have to do with the layering of the elements. Typically this can be fixed by re-layering your HTML such that the anchor ends up on top, or setting it to position: relative.
you might have !important on the unhover which would block the hover.

JavaScript/jQuery: Monitor change to element?

Is there any way (aside from creating a new event) that I can tell when a particular CSS class has been added to an element?
If you don't have access to the code that's adding the class, there's a jQuery plugin called livequery that will allow you to run code when elements are added to the DOM (or when you do things like add a class to an element).
http://brandonaaron.net/code/livequery/docs
$('div.myClass').livequery(function() {
alert('myClass was added');
});
$('#someDiv').addClass("myClass"); // The livequery code will run
This works specifically with jQuery methods.
You could extend jQuery's addClass() to fire an event when it adds a class. However, this means you'll have to add classes always with this method.
If you don't do that, you'll have to poll and look for differences in the class attribute. I don't recommend doing that. Besides performance, you'll need to handle classes being removed too.
There is a DOM Level 3 specification to detect changes to an elements attributes, it is supported in a couple of browsers... Also IE supports an onpropertychange (http://msdn.microsoft.com/en-us/library/ms536956(VS.85).aspx) event too.
It's probably not going to be enough though. Your best bet is use window.setInterval() and check if the value has changed.

Categories