Circular dependency between JavaScript class and jQuery object

I am trying to take an existing working code base and make it object oriented using JavaScript. My system accepts JSON containing groups and elements in a one-to-many relationship, and renders this on the page. Elements can be moved to different groups, and their positioning within these groups must also be calculated. Thus, events that know about groups and tickets around them should be established.

I use John Resig’s simple JavaScript inheritance setup to set the two classes Item and Group . When each Item is created, it returns the parent Group to it. My problem arises when I want to set my events and is most easily explained by the following function:

 var Group = Class.extend({ ... // Calculate where to place the new item within the group calculate_new_position: function(item) { var pos = -1; // Loop through all DOM items in groups body $(".item", this.elBody).each(function(i) { // Retrieve it class object var next = $(this).data("_obj"); // Calculating things using the class reference var lowerPrio = item.tData.priority < next.tData.priority, lowerId = item.id < next.id; if(lowerPrio || lowerId) { pos = i; return false; } }); return pos; }, ... )}; 

Note the use of .data("_obj") in the above snippet. Essentially, when I need to do something like sorting elements, then I need to know the object (model) corresponding to each DOM element (view / controller) in the group. Now I could set my Group class so that when creating each Item I would add a link to it from my Group (for example, Group.items = [i1, i2...] ), and then instead of iterating over the elements DOM, I can iterate through Item instances. However, I think I encountered similar problems, for example, when I want to move an Item to another Group (since the Group would not know about Item ).

In short : is it intrinsically dangerous / naive / pointless to have a class that, when instantiated, creates a DOM element, which then, in turn, points to the class? It looks like a circular dependency and some recursive nightmare, but perhaps an object reference is not such a terrible thing. If I am doing something else stupid, and it’s much simpler there, then point this out as well.

+6
source share
3 answers

Any modern browser garbage collector can handle circular links. Your object will be garbage collected if you both lose all references to the object and delete all HTML nodes from the DOM (while in the DOM, the browser sees a link to your object and does not allow it to collect garbage). However, be careful with event handlers; they can also perform checks if you also do not delete them.

I heard that some older versions of IE have issues with circular references. For a more detailed explanation: Exact JavaScript Explanation ↔ DOM Round Question

From this answer: jQuery.data () makes the thing more reliable, since older versions of IE had a certain problem with the properties that were added to the DOM element, and did not handle circular references that correctly included data in these properties, and thus things would not free when they should eat (leak) .. data () works around this, using only one added property in the DOM element, which is safe, not leak. This line is the key to the javascript object, which can contain all the properties that you want to associate with the DOM element. Since these properties are stored in simple javascript, where browsers do not have circular support errors, this method does not cause leaks.

You might want to learn D3.js, it does something similar to what you want. It associates data with DOM elements and provides an easy way to generate visualizations: http://d3js.org/

Using D3, you can bind an array of numbers to an array of round SVG tags and make each circle have a radius based on the amount of array associated with it.

EDIT: I forgot to mention that if you use $ .remove (), event handlers are also deleted. But if you have, for example, an event handler that is inside the closure that also has a link to your (remote) HTML node, it will not collect garbage. This is usually not a big problem, because when it goes beyond the DOM, it will not consume too many resources, and as far as I know, it is impossible to restore all of these closing links.

+2
source

When using jQuery.data it does nothing with the dom element. The dom element simply stores the entry of the primitive key into the jQuery internal cache, regardless of *. So the link you create is from JS to JS:

 var div = document.createElement("div"); $(div).data("asd", "daa") console.log(div[jQuery.expando]); //41 console.log(jQuery.cache[41].data.asd); //"daa" 

(it is obvious that the above jQuery internal elements should not rely on production code)

However, the data will flow there if you do not go through jQuery to do all your manipulations, because jQuery will not be notified if you go behind it.

The best practice in widget classes is to provide a destroy method that removes all the elements for which it is responsible, unbinds any events associated with it, and sets all of its DOM links to null.

* If there are absolutely no events or data about the item, then the record does not exist. But if the record exists for any reason, then using jQuery.data will not interact with the element itself.

+1
source

@ Hoffman's answer is good, as he offers practical tips with explanations. I offer a different perspective, inspired by how Backbone.js works. This extension is on what @vsr suggested in its original comment.

The main goal: not to store your JS objects in $ .data () at all.

Using $ .data (), you can use $ (foo) to search for DOM objects, then expand in your properties to go to your custom JS functions that can work with a lot of DOM elements to get their custom functions. It is too complicated and inefficient.

The best approach is to create a set of "pure JS" objects using any syntax and mechanism that suits your needs. These objects can then have a property that points to their DOM element for the few cases where it is needed. The backbone (again) assumes that good practice also caches the highlighted jQuery DOM element. Processing these "pure JS" objects will be faster and easier to maintain than with $ .data (). It also enhances the abstraction between your JS and HTML, which is a good thing.

Also, I found a good way to work, it is NOT messing up your objects, even if there is only a one-to-many relationship. Do not "objects" properties of "groups". Instead, maintain arrays of both groups and elements and have one (or both) manage the association by reference rather than by membership.

+1
source

Source: https://habr.com/ru/post/951387/


All Articles