Closed hexpl0it closed 1 day ago
You'll want to use the awaited element as the anchor
tag, and use the append
option to append your UI using insertAdjacentElement
.
Something like this would work. I typed this up in github comments, so it probably isn't valid JS, and I haven't tested it. But hopefully it gives you a better idea of what you're looking for.
let anchor;
const ui = createIntegratedUi(ctx, {
position: "inline",
anchor: () => anchor,
append: (anchor, root) => anchor.insertAdjacentElement("afterend", root),
onMount: async (container) => {
const root = ReactDOM.createRoot(container);
root.render(<Toolbar />);
return root;
},
onRemove: (root) => {
root.unmount();
},
});
watchDomChanges(ctx, '[class^="ToolbarContainer__StyledHeader"]', {
onAdd: (newAnchor) => {
anchor = newAnchor;
ui.mount();
},
onRemove: () => {
ui.remove();
},
});
function watchDomChanges(ctx: any, selector: any, callbacks: any) {
let prevAnchor: HTMLElement | undefined;
const observer = new MutationObserver(() => {
const el = document.querySelector(selector);
if (el && !prevAnchor) {
callbacks.onAdd(el);
} else if (!el && prevAnchor) {
callbacks.onRemove();
}
prevAnchor = el;
});
ctx.onInvalidated(() => observer.disconnect());
observer.observe(document.body, {
childList: true,
subtree: true,
});
const initialEl = document.querySelector(selector);
if (initialEl) {
callbacks.onAdd(initialEl);
prevAnchor = initialEl;
}
}
@hexpl0it If you get something that works, I'd like to add auto-mounting/unmounting to WXT, if you would like to contribute your implementation.
Thank you very much for this issue. I have recently encountered a similar problem.
I want to inject content script above a specific div on the website after it has loaded.
I have tried the methods, which are effective. Thanks for @aklinker1 .
I wonder if there are other methods that can achieve similar functions? Maybe similar effects can be achieved without using an anchor
?
Think I'm gonna work on this soon. Here's what I'm thinking the API will look like:
const ui = createXyzUi({
// ...
anchor: "#some-anchor",
})
ui.autoMount();
Here's the types I expect:
type StopAutoMount = () => void;
interface UI {
autoMount(options?: { once?: boolean }): StopAutoMount;
}
I've been thinking about this Issue in the back of my mind for a long time. We get a lot of questions about dynamic UI mounts.
I just wonder what and how WXT should support. I have a feeling that autoMount
will have a lot of responsibility since the timing and conditions are different for each application.
BTW: Actually, I have a personal library for dynamic mounting, lol.
I have a feeling that
autoMount
will have a lot of responsibility since the timing and conditions are different for each.
Hmm, I recommended this function to addressing a single use case: mounting a UI inside a dynamic element that gets added and removed.
If someone wants a custom implementation, like listening to focus events, or adding timeouts, they should write those themselves using ui.mount
and ui.unmount
. that's why we provide those APIs.
BTW: Actually, I have a personal library for dynamic mounting, lol.
Nice! We can probably use it, does it support listening for when the element is removed from the DOM?
If someone wants a custom implementation, like listening to focus events, or adding timeouts, they should write those themselves using ui.mount and ui.unmount. that's why we provide those APIs.
If you are set in your mind on that policy, it's ok 👍 . Writing in the document that it is a single use case would avoid confusion users.
does it support listening for when the element is removed from the DOM?
Supported by detector
option. However, this library doesn't manage DOM state. This is an async wrapper for pure mutationObserver
and querySelector
. So if you want accuracy based on DOM state, will need a separate DOM state manager.
Basically, it goes like this. (recently, major bump to v4, so if there any bugs it might not work properly 😅 )
// waiting anchor
const anchor = await waitElement(targetAnchor);
if (anchor) {
ui.mount();
}
// waiting remove
const nowAnchor = await waitElement(targetAnchor, { detector: isNotExist });
if (nowAnchor === null) {
ui.unmount()
}
I am onboard with adding functionality to WXT.
Yeah, I’m willing to take this. I recently made my library support xpath for this issue :). I'm trying to write the test code first……but recently I've been so busy that I haven't had time. Need more time.
Interesting discussion - I'm doing similar stuff - following :-)
@aklinker1 I'm working on the implementation a bit at a time. So we need to some decisions about the specs. About once
option API, is this “only one mount” or “one mount and one remove”? Which did you expect?
And I think about the StopAutoMount
, should be stopped when ui.remove
is called, is there any discrepancy?
About once option API, is this “only one mount” or “one mount and one remove”? Which did you expect?
I would expect it to be mounted and removed once.
And I think about the
StopAutoMount
, should be stopped whenui.remove
is called, is there any discrepancy?
Yes, I agree with you on that. Calling ui.remove()
should stop the auto-mount watcher as well.
Okay, I'll go ahead with those specs for both!
For my extension I need to mount my app immediately after a precise div. However, this div is not immediately available when the page is loaded.
To do this I used MutationObserver:
However, this div can be removed following certain actions on the page. I would need to reassemble my component as soon as this div reappears. How can I do this?