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

Include instance and window on HookOptions #301

Conversation

westonruter
Copy link
Contributor

When experimenting with hooks, I found the context provided in HookOptions to be insufficient for some use cases. For example, I wanted to prevent a script from calling alert() by replacing the call with the construction of a dialog using an apply hook:

<script>
var partytown = {
  apply(opts) {
    if (opts.name === 'alert') {
      const doc = opts.window.document;
      const dialog = doc.createElement('dialog');
      dialog.open = true;
      dialog.appendChild( doc.createTextNode(opts.args[0]) );
      doc.body.appendChild(dialog);
      return undefined;
    }
    return opts.continue;
  },
};
</script>

I found this wasn't possible because the proxied DOM is not exposed to the hook (which I'm here accessing via opts.window.document). So this PR exposes the window as a property on HookOptions.

Another use was trying to solve for was to restrict DOM mutations to the subtree of a given element. I found this was not possible because the instance is not exposed on HookOptions. When it is, I can walk up the DOM node ancestor in a hook to determine if the hook should be prevented or not:

<script>
  var partytown = {
    apply(opts) {
      const { instance } = opts;

      const mutationMethods = new Set(['insertBefore', /*...*/]);
      if (!mutationMethods.has(opts.name)) {
        return opts.continue;
      }

      let isInsideDocument = false;
      let isInsideSandbox = false;
      if ('parentNode' in opts.instance) { // @todo This fails for the style properties which is a CSSStyleDeclaration.
        let parent = instance;
        while (parent) {
          if (parent == instance.ownerDocument.documentElement ) {
            isInsideDocument = true;
          }
          if ( parent.id === 'sandbox' ) {
            isInsideSandbox = true;
          }
          parent = parent.parentNode;
        }
        if ( ! isInsideDocument ) {
          return opts.continue;
        }
        if ( ! isInsideSandbox )  {
          return null;
        }
      }
      return opts.continue;
    },
  };
</script>

(As the @todo notes, this is not going to catch all cases. For example, when mutating an inline style there is no parentNode property available for the CSSStyleDeclaration instance being modified.)

@vercel
Copy link

vercel bot commented Nov 14, 2022

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated
partytown ✅ Ready (Inspect) Visit Preview Nov 14, 2022 at 7:52PM (UTC)

@adamdbradley
Copy link
Contributor

Awesome thanks!

westonruter added a commit to westonruter/partytown that referenced this pull request Nov 17, 2022
* origin/main:
  docs: ptupdate custom event
  docs: types
  0.7.2
  docs: Updated weback copy plugin example code (BuilderIO#291)
  docs: Fix capitalization of WordPress (BuilderIO#275)
  chore: Add nvm config file (BuilderIO#276)
  docs: Fixed typos (BuilderIO#285)
  docs: Update nuxt.md (BuilderIO#297)
  feat: Include instance and window on HookOptions (BuilderIO#301)
  feat: update after dynamic script append
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants