morphdom icon indicating copy to clipboard operation
morphdom copied to clipboard

onAfterChanges option (or something similar)

Open StefanJelner opened this issue 3 years ago • 7 comments

First of all thanks for this great library. I am using it for small reactive widgets in websites and it works like a charm.

In some scenarios i need to know, when morphing is completely finished. My approach now is to add onElUpdated, onNodeAdded and onNodeDiscarded and inside of the callbacks fire another callback which is throttled by request animation frame. This works for me, but is a little bit hacky in my opinion.

Would it be possible to provide an option - like onAfterChanges - which becomes invoked after everything has been done?

StefanJelner avatar Feb 04 '22 09:02 StefanJelner

I've had a need today for onAfterChanges to reorder the output, because nodes I prevent from being removed and newly inserted nodes end up in the wrong order (I can't change the HTML structure so have to fix in JS).

Adding this to morphdom would be very useful. In the meantime, are you able to share your working but hacky code?

pbowyer avatar Feb 09 '22 20:02 pbowyer

Yes. Of course. What i do is to throttle all calls of onElUpdated, onNodeAdded and onNodeDiscarded. You can do this with the library raf-throttle to throttle it to the request animation frame or you can use something like throttle-debounce to do it by an amount of milliseconds.

Example:

import rafThrottle from 'raf-throttle';

const onAfterChanges = rafThrottle(() => console.log('onAfterChanges'));

morphdom(
    document.getElementById('foo'),
    '<div>bar</div>',
    {
        onElUpdated: () => onAfterChanges(),
        onNodeAdded: node => { onAfterChanges(); return node; },
        onNodeDiscarded: node => { onAfterChanges(); return node; }
    }
);

What happens is, that the throttled function onAfterChanges will get invoked again and again, but waits until nothing happens for a request animation frame. Then it will finally be invoked.

If you have further questions, feel free to ask. :smile:

StefanJelner avatar Feb 10 '22 17:02 StefanJelner

onAfterChanges I like the suggestion. I'd certainly be open for a PR. If one doesn't come across, I'll try and get to it this month.

snewcomer avatar Mar 08 '22 13:03 snewcomer

I'd love to see this implemented, so bump.

One thing that I'd strongly suggest is that such an "after" callback fire even if no changes are performed. We're currently dealing with the opposite situation eg. we need to run some cleanup after morphdom does its thing, but if nothing is changed then onElUpdated never runs.

leastbad avatar May 27 '22 08:05 leastbad

Building on @StefanJelner's solution - while acknowledging that I'm hoping for an outcome where a callback fires after processing even if there are no changes - is it fair and reasonable to say that a final call to onAfterChanges() immediately before line 753 would do the trick?

leastbad avatar May 27 '22 09:05 leastbad

If onAfterChanges() gets the changes as an argument, then it is possible to decide, whether to do something or not, based on the content of the argument. F.ex. if the argument is an empty array, then it is clear, that actually nothing changed, while if it is an array of changes (how they look like might be another discussion), then i know that something changed. And maybe in some scenarios it is useful to know what exactly changed. Just my thoughts.

StefanJelner avatar May 31 '22 13:05 StefanJelner

I share your desire; it would be nice. In fact, I'm implementing a comparison concept in a library that uses morphdom right now.

Thing is, I would rather have morphdom be super fast than provide a changeset. I genuinely worry that making those changes available would require a lot more code and a lot more CPU cycles to run that code. (I'd be thrilled to be wrong.)

In our library, given that this functionality hasn't been merged and released yet, we're making use of the techniques discussed earlier in this thread - paired with an initial check where we compare the source fragment against the innerHTML of the target element. We figure that even if this is a heavy operation, it's still less heavy than calling morphdom if we know the source and target are already equal.

If this functionality does get merged down the line, we'll take that as an opportunity to simplify our local implementation by adopting the morphdom onAfterChanges event.

leastbad avatar May 31 '22 14:05 leastbad