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

Greedy script? #17

Closed
ZzZombo opened this issue Dec 21, 2017 · 17 comments
Closed

Greedy script? #17

ZzZombo opened this issue Dec 21, 2017 · 17 comments
Milestone

Comments

@ZzZombo
Copy link

ZzZombo commented Dec 21, 2017

var observer = new MutationObserver(function (mutations) {

        // For each MutationSelectorObserver currently registered.
        for (var j = 0; j < msobservers.length; j++) {
            $(msobservers[j].selector).each(msobservers[j].callback);
        }
    });

If I read the code right, nowhere there the mutations variable is ever referenced. How does the script work then? Does it mean that for all mutations it subscribed to to all elements in the document, it will run callbacks? Huh? That explains the huge performance hit I witness ever since I added the script to my userscript.

@bezborodow
Copy link
Collaborator

Hi @ZzZombo. Your performance issue sounds plausible. I'll be looking into it soon.

@bezborodow
Copy link
Collaborator

@bezborodow
Copy link
Collaborator

bezborodow commented Jan 2, 2018

There are a few issues here (without delving into the code):

  1. Subscription to all mutations. According to documentation, there are three mutation types: childList, attributes, or characterData. The script needs to observe attributes for situations such as when the class or id attributes are changed. childList must be observed for when a new element is added to the document. We could probably ignore characterData, so we can change that.

  2. The entire document is observed. There is the possibility to add extra options to limit this to specific elements, such as <body> or the main container <div>, etc. This isn't a major issue anyhow.

  3. When a mutation is observed, we search the entire document for matching elements. This could be limited to the element where the mutation occured. However, this could break selectors that use child or descendant combinators. So, this would probably need to be an optional feature to ensure backward compatibility.

@ZzZombo
Copy link
Author

ZzZombo commented Jan 2, 2018

This shouldn't break anything. Nodes are already inserted/removed by the time an observer handles such actions. Same goes for all the other event types.

@pati610
Copy link

pati610 commented Feb 18, 2018

@ZzZombo I have 50+ selectors registered, and have no impacts on performance whatsoever. Are you sure that you're only initializing your selectors once? Because if they get reinitialized, let's say at partial loads on a dynamic page, that will choke the page in no time.

I know this from my own experience, because I have to do just that - detect newly inserted DOM elements at every partial ajax page load. The site went crazy when selectors were registered. I wrote a deInitialize func, removing either all or defined selectors, that I attached to a jQuery on hashchange callback. Works great!

@pie6k I'll fork your script and then I'll make a pull request, you are free to incorporate my added func as you see fit (if at all.)

@pati610 pati610 mentioned this issue Feb 18, 2018
@ZzZombo
Copy link
Author

ZzZombo commented Feb 19, 2018

The amount of selectors has no impact on performance whatsoever. It's the actual DOM that matters, whether it's a single element "Hello, world" page or both element heavy and dynamically changing one makes huge difference. Using this library in my userscript had actually made my browser ask me if I want to kill unresponsive page or not several times. I no longer use it, having learnt to use the MutationObserver API directly, and while the browser may choke sometimes on complex pages, it still yields better response times overall over the previous version of my script.

@bezborodow
Copy link
Collaborator

bezborodow commented Feb 20, 2018

Edit: Deleted to avoid confusion. The content of this post is out-of-date.

@bezborodow
Copy link
Collaborator

The third feature has also now been implemented in #19.

@bezborodow
Copy link
Collaborator

bezborodow commented Feb 22, 2018

Further improvements in #21.

@ZzZombo If you are interested in testing, please test the branch in #21, dbezborodovrp:feature/autoperf

To test via NPM:

npm install github:dbezborodovrp/jquery.initialize#feature/autoperf --save && npm install

@ZzZombo
Copy link
Author

ZzZombo commented Feb 22, 2018

You made the wrong call. Please read the API specs, or just do a simple test with debug output to browser console. When MutationObserver fires a callback, all changes in the batch it provides have ALREADY executed, as I have said it so in my first response 😩. That's why you are provided with the changed element and optionally **old**-whatever attribute values and not the other way around, element to be changed, and **new**-something values.

@bezborodow
Copy link
Collaborator

Have you seen and tested the changes?

I'm not sure I follow your reasoning. I know that the element has already been mutated before the observer sees it. That's not a problem. If fact, if the change were not visible, then it would be impossible to query the mutated nodes for matches. So, this seems completely irrelevant to me.

I may have not explained my changes correctly. Disregard the above. I will explain it afresh.

What I have done in #19 and #21 is to address the following concerns:

Does it mean that for all mutations

The plugin now ignores mutation types that are not childList or attributes

it subscribed to to all elements in the document

By default, the plugin will observe all elements (that is, document.documentElement. However, you can now override this by specifying an element. For example:

$.initialize.defaults.target = document.getElementById('observe-this-element');

Or:

$.initialize('div.foo', callbackFunction, { target: document.getElementById('observe-this-element') });

Furthermore, the plugin will not query the entire document for nodes matching the selector. It will only check the mutated node for a match (or its descendants or parents depending upon the complexity of the selector.)

@bezborodow
Copy link
Collaborator

Did you want to be able to set attributeOldValue to true in the MutationObserverInit options? I can expose that if that is what you are after.

@ZzZombo
Copy link
Author

ZzZombo commented Feb 22, 2018

$.initialize.defaults.scanDocument = false; is not needed is all I say.

@bezborodow
Copy link
Collaborator

I agree. I removed that in #21.

@ZzZombo
Copy link
Author

ZzZombo commented Feb 22, 2018

Then great. Hope to use the updated version in a project soon.

@bezborodow
Copy link
Collaborator

Very good. The performance improvements haven't been released yet. If you want to test with your application, please do so by installing the test branch:

npm install github:dbezborodovrp/jquery.initialize#feature/autoperf --save && npm install

I also exposed observer options in 573fb61

@bezborodow bezborodow added this to the 1.2.0 milestone Apr 10, 2018
@bezborodow
Copy link
Collaborator

bezborodow commented Apr 10, 2018

These performance changes have been released under 1.2.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants