empyreal
empyreal

Reputation: 177

Access to internal function from externally loaded HTML in React component

I have the following use case.

Some HTML from a third party source is loaded inside my React component:

class MyComponent extends Component {
  render() {
    return (
      <div
        dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
      />
    );
  }
}

Inside the externally loaded HTML a click event exists for a specific span, which is supposed to call a callback function that exists in my application.

<span onclick="myCallback(param1='asd', param2=123, param3='asdas')">
  Click me!
</span>

Where should I put this myCallback function?

If I place it inside the component class I get the following error when clicking the span, because as I understand the function is not visible to the externally loaded HTML: Uncaught ReferenceError: myCallback is not defined at HTMLSpanElement.onclick

My other idea was to add the function to the window object window.myCallback = ... inside my main index.js file to be loaded every time the app loads. This way it works but I have two issues.

Any suggestions?

Upvotes: 4

Views: 1100

Answers (1)

thinhvo0108
thinhvo0108

Reputation: 2232

Using "dangerouslySetInnerHTML" is ..."dangerous" as its name ^^, which is actually not pure React way, either.

However, If you have to do it, you can do something like this (take advantage of built-in jQuery inside React be default)

=====

EDITED VERSION FROM HERE: (use only 1 component)

export default class MyComponent extends Component {
  componentDidMount() {
    // using jQuery to manipulate DOM element form third-party source
    // NB. you may think of using setTimeout here, to wait for the external source to be fully loaded here, of course it's not so safe
    // but anyway, we are using "dangerouslySetInnerHTML" anyway => quite dangerous, though ^^  

    // setTimeout(function(){   
      $(document.findElementsByTagName("span")[0]).click(function(e){ 
      // or perhaps $("#spanID").click if you can, just be careful between DOM element and react component

          e.preventDefault();
          // DO SOMETHING HERE, just like you do in the window.onload function
          // or maybe you also need to get param values by getting $(this).data("...") or $(this).attr("ATTRIBUTE-NAME")
          return false;

      });
    // });
  }
  render() {
    return (
      <div
        dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
      />
    );
  }
}   

=====

OLD ANSWER FROM HERE: (use 2 components)

ParentComponent:

export default class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.callbackOnThisComponent = this.callbackOnThisComponent.bind(this);
  }
  callbackOnThisComponent(param1, param2, param3) {
    // do whatever you like with the above params
  }
  render() {
    return (
      <ChildComponent triggerCallbackOnParent={this.callbackOnThisComponent} />
    );
  }
}

ChildComponent:

export default class ChildComponent extends Component {
  componentDidMount() {
    // using jQuery to manipulate DOM element form third-party source

    let that = this;

    // NB. you may think of using setTimeout here, to wait for the external source to be fully loaded here, of course it's not so safe
    // but anyway, we are using "dangerouslySetInnerHTML" anyway => quite dangerous, though ^^  

    $(document.findElementsByTagName("span")[0]).click(function(e){ 
    // or perhaps $("#spanID").click if you can, just be careful between DOM element and react component

        e.preventDefault();
        that.props.triggerCallbackOnParent(param1, param2, param3);
        // or maybe you need to get param values by getting $(this).data("...") or $(this).attr("ATTRIBUTE-NAME")
        return false;

    }, that);

  }
  render() {
    return (
      <div
        dangerouslySetInnerHTML={{ __html: this.props.externalHTML }}
      />
    );
  }
}   

I just use the main React's idea, which is passing props downward to children components, and when you want to trigger a function upward from child component, create a callback function on parent. For your or anyone else's reference, this is my demonstration on how to pass callback function from parent to multi-level-children components:

Force React container to refresh data

Re-initializing class on redirect

if this doesn't work yet, feel free to show me some error logs, thanks

Upvotes: 1

Related Questions