1

There is an iFrame with buttons inside that I cannot change directly. The requirement is to use this specific iFrame. Inside this iFrame, there have many buttons with the same classname that the user can click to navigate to another page. Outside the iFrame, I have a form of user data and tracking to know when the user has made any changes.

Note: I'm not actually using an iFrame, but the basic idea is I have content that I cannot directly change.

I have a message when the user has unsaved changes and they click on any item inside the iFrame ~ "You have unsaved changes, are you sure you want to discard them?".

I would like to override the original function's definition to have my blocking confirmation logic in front of whatever logic is already there. When the user confirms, I need to continue the original navigation.

The best way I can think to do this is to query the button element and override the button.click() function with an my popup message and then the original function call. Maybe something like this:

useEffect(()=>{
   const elements = document.getElementsByClassname("classname");
   for (let element of elements){
      const originalFunction = element.onclick // <--
      element.onclick = () => {
         if (blocked) {
            confirmation({
               content: "You have unsaved changes, are you sure you want to discard them?",
               proceed: "Discard changes",
               cancel: "Cancel",
               handler: function () {
                  discardChanges();
                  originalFunction(); // <--
               }
            });
         } else {
             originalFunction(); // <--
         }
      }
   }
   return ()=>{
      restoreOriginalFunctionDefinitions(); // <--
   }
},[]);
  1. What is the correct way to inside a function in-front of another function definition?
  2. Is it possible to do this and then restore the original definition when the element unmounts?
  3. Is there just a better way to do this?

I've tried the following useEffect

    useEffect(()=>{
        const elements = Array.from(document.getElementsByClassName(watchingClassName));
        console.info("elements", elements);
        elements.forEach((element)=>{
            const htmlElement = element as HTMLElement;
            AdviceFunction(htmlElement.click, blocking, "Are you sure", "Discard",onDiscard,"Cancel" );
        });
    }, [])

Advice Function has the following definition:

const AdviceFunction = (
    originalFunction,
    blocked,
    message,
    discardCaption,
    discardAction,
    cancelCaption
) =>
    function (...args) {
        console.info("advice", { this: this, originalFunction, blocked });
        if (blocked) {
            confirmation({
                content: message,
                proceed: discardCaption,
                cancel: cancelCaption,
                handler: function () {
                    discardChanges(discardAction);
                    originalFunction.apply(this, args);
                }
            });
        } else {
            originalFunction.apply(this, args);
        }
    };

Actual result: useEffect successfully queries the dom for the buttons. When a button is clicked, only the original function is ran and the console.info in AdviceFunction is never logged.

1 Answers1

0

I came up with another approach that works.

  1. Use a class name with document.getElementsByClassname to get a list of content to supersede
  2. Iterate that list to create components that have the same position and dimensions as the elements retrieved (position fixed, top, left, width, height, opacity: 0)
  3. When the user clicks on the component, record the coordinates of the click event then show the confirmation message
  4. If the user confirms the navigation, use document.elementsFromPoint(x,y)1 to get the first content underneath the generated components then click that content

See code here:

Back to the original example, I can simply add a class to the iFrame itself and any other buttons I want to block to cover it with a transparent div.

In the screenshot, the red content is what was retrieved by the class name

enter image description here

When the user clicks on any red container, it shows the confirmation.