React—add a click listener to dangerouslySetInnerHTML sub-element











up vote
0
down vote

favorite












I have this html text:



"<span id='capo' class='capo' data-capo=3>Capo 3</span>"


This is part of a larger html that is inserted via dangerouslySetInnerHTML



And I have a listener like this:



componentDidMount() {
document.getElementById('capo').addEventListener('click', this.changeKey);
}


However this only works for one click—after that the listener doesn't exist. I'm guessing because React has replaced the component (since I change the state in this.changeKey), and therefore the listener no longer exists.



How do I reliably set a listener for React components?










share|improve this question


























    up vote
    0
    down vote

    favorite












    I have this html text:



    "<span id='capo' class='capo' data-capo=3>Capo 3</span>"


    This is part of a larger html that is inserted via dangerouslySetInnerHTML



    And I have a listener like this:



    componentDidMount() {
    document.getElementById('capo').addEventListener('click', this.changeKey);
    }


    However this only works for one click—after that the listener doesn't exist. I'm guessing because React has replaced the component (since I change the state in this.changeKey), and therefore the listener no longer exists.



    How do I reliably set a listener for React components?










    share|improve this question
























      up vote
      0
      down vote

      favorite









      up vote
      0
      down vote

      favorite











      I have this html text:



      "<span id='capo' class='capo' data-capo=3>Capo 3</span>"


      This is part of a larger html that is inserted via dangerouslySetInnerHTML



      And I have a listener like this:



      componentDidMount() {
      document.getElementById('capo').addEventListener('click', this.changeKey);
      }


      However this only works for one click—after that the listener doesn't exist. I'm guessing because React has replaced the component (since I change the state in this.changeKey), and therefore the listener no longer exists.



      How do I reliably set a listener for React components?










      share|improve this question













      I have this html text:



      "<span id='capo' class='capo' data-capo=3>Capo 3</span>"


      This is part of a larger html that is inserted via dangerouslySetInnerHTML



      And I have a listener like this:



      componentDidMount() {
      document.getElementById('capo').addEventListener('click', this.changeKey);
      }


      However this only works for one click—after that the listener doesn't exist. I'm guessing because React has replaced the component (since I change the state in this.changeKey), and therefore the listener no longer exists.



      How do I reliably set a listener for React components?







      reactjs






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 11 at 7:41









      Mirror318

      4,84922941




      4,84922941
























          2 Answers
          2






          active

          oldest

          votes

















          up vote
          1
          down vote













          In this case you can make use of event bubbling!



          Just add your event listener to the upper element (the one with dangerouslySetInnerHTML will be fine) listen for clicks and make sure that e.target.id is equal to the desired element ID.



          Check out the example below (it uses dangerouslySetInnerHTML just to show how event bubbling works and how to use it with React, you shouldn't write component like this in the real life):






          class Element extends React.Component {
          state = {
          loadTime: 0,
          lastUpdate: 0
          };

          lastUpdateTimer = null;

          componentDidMount() {
          this.resetTimer();

          this.lastUpdateTimer = setInterval(this.setLastUpdate, 1000);

          this.dynamicContentElement.addEventListener("click", this.handleClick)
          }

          componentWillUnmount() {
          clearInterval(this.lastUpdateTimer);
          this.dynamicContentElement.removeEventListener(this.handleClick)
          }

          resetTimer() {
          const now = Date.now();

          this.setState({
          loadTime: now,
          lastUpdate: now
          });
          }

          setLastUpdate = () => {
          this.setState({
          lastUpdate: Date.now()
          });
          }

          getDynamicContent() {
          const time = Math.round((this.state.lastUpdate - this.state.loadTime) / 1000);
          return {
          __html: `<p>
          You are here: <strong>${time}</strong> seconods.
          <br>
          <button id="btn">Click to reset counter</button>
          </p>`
          };
          }

          handleClick = (e) => {
          if (e.target.id === "btn") {
          this.resetTimer();
          }
          }

          render() {
          return (
          <div>
          Dynamic content below:
          <div
          ref={el => this.dynamicContentElement = el}
          dangerouslySetInnerHTML={this.getDynamicContent()}
          />
          </div>
          );
          }
          }


          ReactDOM.render(
          <Element />,
          document.getElementById('root')
          );

          <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
          <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
          <div id="root"></div>








          share|improve this answer




























            up vote
            0
            down vote













            Whenever there is a state change, React removes the html and replaces it.



            This code will only execute when the component is first loaded, not when state changes and html elements (and their listeners) are wiped:



            componentDidMount() {
            document.getElementById('capo').addEventListener('click', this.changeKey);
            }


            But this code will run every time the component is rerendered (so whenever a new html element is made, a new listener is attached to it):



            componentDidUpdate() {
            document.getElementById('capo').addEventListener('click', this.changeKey);
            }





            share|improve this answer





















              Your Answer






              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "1"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              convertImagesToLinks: true,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: 10,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














               

              draft saved


              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53246760%2freact-add-a-click-listener-to-dangerouslysetinnerhtml-sub-element%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes








              up vote
              1
              down vote













              In this case you can make use of event bubbling!



              Just add your event listener to the upper element (the one with dangerouslySetInnerHTML will be fine) listen for clicks and make sure that e.target.id is equal to the desired element ID.



              Check out the example below (it uses dangerouslySetInnerHTML just to show how event bubbling works and how to use it with React, you shouldn't write component like this in the real life):






              class Element extends React.Component {
              state = {
              loadTime: 0,
              lastUpdate: 0
              };

              lastUpdateTimer = null;

              componentDidMount() {
              this.resetTimer();

              this.lastUpdateTimer = setInterval(this.setLastUpdate, 1000);

              this.dynamicContentElement.addEventListener("click", this.handleClick)
              }

              componentWillUnmount() {
              clearInterval(this.lastUpdateTimer);
              this.dynamicContentElement.removeEventListener(this.handleClick)
              }

              resetTimer() {
              const now = Date.now();

              this.setState({
              loadTime: now,
              lastUpdate: now
              });
              }

              setLastUpdate = () => {
              this.setState({
              lastUpdate: Date.now()
              });
              }

              getDynamicContent() {
              const time = Math.round((this.state.lastUpdate - this.state.loadTime) / 1000);
              return {
              __html: `<p>
              You are here: <strong>${time}</strong> seconods.
              <br>
              <button id="btn">Click to reset counter</button>
              </p>`
              };
              }

              handleClick = (e) => {
              if (e.target.id === "btn") {
              this.resetTimer();
              }
              }

              render() {
              return (
              <div>
              Dynamic content below:
              <div
              ref={el => this.dynamicContentElement = el}
              dangerouslySetInnerHTML={this.getDynamicContent()}
              />
              </div>
              );
              }
              }


              ReactDOM.render(
              <Element />,
              document.getElementById('root')
              );

              <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
              <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
              <div id="root"></div>








              share|improve this answer

























                up vote
                1
                down vote













                In this case you can make use of event bubbling!



                Just add your event listener to the upper element (the one with dangerouslySetInnerHTML will be fine) listen for clicks and make sure that e.target.id is equal to the desired element ID.



                Check out the example below (it uses dangerouslySetInnerHTML just to show how event bubbling works and how to use it with React, you shouldn't write component like this in the real life):






                class Element extends React.Component {
                state = {
                loadTime: 0,
                lastUpdate: 0
                };

                lastUpdateTimer = null;

                componentDidMount() {
                this.resetTimer();

                this.lastUpdateTimer = setInterval(this.setLastUpdate, 1000);

                this.dynamicContentElement.addEventListener("click", this.handleClick)
                }

                componentWillUnmount() {
                clearInterval(this.lastUpdateTimer);
                this.dynamicContentElement.removeEventListener(this.handleClick)
                }

                resetTimer() {
                const now = Date.now();

                this.setState({
                loadTime: now,
                lastUpdate: now
                });
                }

                setLastUpdate = () => {
                this.setState({
                lastUpdate: Date.now()
                });
                }

                getDynamicContent() {
                const time = Math.round((this.state.lastUpdate - this.state.loadTime) / 1000);
                return {
                __html: `<p>
                You are here: <strong>${time}</strong> seconods.
                <br>
                <button id="btn">Click to reset counter</button>
                </p>`
                };
                }

                handleClick = (e) => {
                if (e.target.id === "btn") {
                this.resetTimer();
                }
                }

                render() {
                return (
                <div>
                Dynamic content below:
                <div
                ref={el => this.dynamicContentElement = el}
                dangerouslySetInnerHTML={this.getDynamicContent()}
                />
                </div>
                );
                }
                }


                ReactDOM.render(
                <Element />,
                document.getElementById('root')
                );

                <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
                <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
                <div id="root"></div>








                share|improve this answer























                  up vote
                  1
                  down vote










                  up vote
                  1
                  down vote









                  In this case you can make use of event bubbling!



                  Just add your event listener to the upper element (the one with dangerouslySetInnerHTML will be fine) listen for clicks and make sure that e.target.id is equal to the desired element ID.



                  Check out the example below (it uses dangerouslySetInnerHTML just to show how event bubbling works and how to use it with React, you shouldn't write component like this in the real life):






                  class Element extends React.Component {
                  state = {
                  loadTime: 0,
                  lastUpdate: 0
                  };

                  lastUpdateTimer = null;

                  componentDidMount() {
                  this.resetTimer();

                  this.lastUpdateTimer = setInterval(this.setLastUpdate, 1000);

                  this.dynamicContentElement.addEventListener("click", this.handleClick)
                  }

                  componentWillUnmount() {
                  clearInterval(this.lastUpdateTimer);
                  this.dynamicContentElement.removeEventListener(this.handleClick)
                  }

                  resetTimer() {
                  const now = Date.now();

                  this.setState({
                  loadTime: now,
                  lastUpdate: now
                  });
                  }

                  setLastUpdate = () => {
                  this.setState({
                  lastUpdate: Date.now()
                  });
                  }

                  getDynamicContent() {
                  const time = Math.round((this.state.lastUpdate - this.state.loadTime) / 1000);
                  return {
                  __html: `<p>
                  You are here: <strong>${time}</strong> seconods.
                  <br>
                  <button id="btn">Click to reset counter</button>
                  </p>`
                  };
                  }

                  handleClick = (e) => {
                  if (e.target.id === "btn") {
                  this.resetTimer();
                  }
                  }

                  render() {
                  return (
                  <div>
                  Dynamic content below:
                  <div
                  ref={el => this.dynamicContentElement = el}
                  dangerouslySetInnerHTML={this.getDynamicContent()}
                  />
                  </div>
                  );
                  }
                  }


                  ReactDOM.render(
                  <Element />,
                  document.getElementById('root')
                  );

                  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
                  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
                  <div id="root"></div>








                  share|improve this answer












                  In this case you can make use of event bubbling!



                  Just add your event listener to the upper element (the one with dangerouslySetInnerHTML will be fine) listen for clicks and make sure that e.target.id is equal to the desired element ID.



                  Check out the example below (it uses dangerouslySetInnerHTML just to show how event bubbling works and how to use it with React, you shouldn't write component like this in the real life):






                  class Element extends React.Component {
                  state = {
                  loadTime: 0,
                  lastUpdate: 0
                  };

                  lastUpdateTimer = null;

                  componentDidMount() {
                  this.resetTimer();

                  this.lastUpdateTimer = setInterval(this.setLastUpdate, 1000);

                  this.dynamicContentElement.addEventListener("click", this.handleClick)
                  }

                  componentWillUnmount() {
                  clearInterval(this.lastUpdateTimer);
                  this.dynamicContentElement.removeEventListener(this.handleClick)
                  }

                  resetTimer() {
                  const now = Date.now();

                  this.setState({
                  loadTime: now,
                  lastUpdate: now
                  });
                  }

                  setLastUpdate = () => {
                  this.setState({
                  lastUpdate: Date.now()
                  });
                  }

                  getDynamicContent() {
                  const time = Math.round((this.state.lastUpdate - this.state.loadTime) / 1000);
                  return {
                  __html: `<p>
                  You are here: <strong>${time}</strong> seconods.
                  <br>
                  <button id="btn">Click to reset counter</button>
                  </p>`
                  };
                  }

                  handleClick = (e) => {
                  if (e.target.id === "btn") {
                  this.resetTimer();
                  }
                  }

                  render() {
                  return (
                  <div>
                  Dynamic content below:
                  <div
                  ref={el => this.dynamicContentElement = el}
                  dangerouslySetInnerHTML={this.getDynamicContent()}
                  />
                  </div>
                  );
                  }
                  }


                  ReactDOM.render(
                  <Element />,
                  document.getElementById('root')
                  );

                  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
                  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
                  <div id="root"></div>








                  class Element extends React.Component {
                  state = {
                  loadTime: 0,
                  lastUpdate: 0
                  };

                  lastUpdateTimer = null;

                  componentDidMount() {
                  this.resetTimer();

                  this.lastUpdateTimer = setInterval(this.setLastUpdate, 1000);

                  this.dynamicContentElement.addEventListener("click", this.handleClick)
                  }

                  componentWillUnmount() {
                  clearInterval(this.lastUpdateTimer);
                  this.dynamicContentElement.removeEventListener(this.handleClick)
                  }

                  resetTimer() {
                  const now = Date.now();

                  this.setState({
                  loadTime: now,
                  lastUpdate: now
                  });
                  }

                  setLastUpdate = () => {
                  this.setState({
                  lastUpdate: Date.now()
                  });
                  }

                  getDynamicContent() {
                  const time = Math.round((this.state.lastUpdate - this.state.loadTime) / 1000);
                  return {
                  __html: `<p>
                  You are here: <strong>${time}</strong> seconods.
                  <br>
                  <button id="btn">Click to reset counter</button>
                  </p>`
                  };
                  }

                  handleClick = (e) => {
                  if (e.target.id === "btn") {
                  this.resetTimer();
                  }
                  }

                  render() {
                  return (
                  <div>
                  Dynamic content below:
                  <div
                  ref={el => this.dynamicContentElement = el}
                  dangerouslySetInnerHTML={this.getDynamicContent()}
                  />
                  </div>
                  );
                  }
                  }


                  ReactDOM.render(
                  <Element />,
                  document.getElementById('root')
                  );

                  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
                  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
                  <div id="root"></div>





                  class Element extends React.Component {
                  state = {
                  loadTime: 0,
                  lastUpdate: 0
                  };

                  lastUpdateTimer = null;

                  componentDidMount() {
                  this.resetTimer();

                  this.lastUpdateTimer = setInterval(this.setLastUpdate, 1000);

                  this.dynamicContentElement.addEventListener("click", this.handleClick)
                  }

                  componentWillUnmount() {
                  clearInterval(this.lastUpdateTimer);
                  this.dynamicContentElement.removeEventListener(this.handleClick)
                  }

                  resetTimer() {
                  const now = Date.now();

                  this.setState({
                  loadTime: now,
                  lastUpdate: now
                  });
                  }

                  setLastUpdate = () => {
                  this.setState({
                  lastUpdate: Date.now()
                  });
                  }

                  getDynamicContent() {
                  const time = Math.round((this.state.lastUpdate - this.state.loadTime) / 1000);
                  return {
                  __html: `<p>
                  You are here: <strong>${time}</strong> seconods.
                  <br>
                  <button id="btn">Click to reset counter</button>
                  </p>`
                  };
                  }

                  handleClick = (e) => {
                  if (e.target.id === "btn") {
                  this.resetTimer();
                  }
                  }

                  render() {
                  return (
                  <div>
                  Dynamic content below:
                  <div
                  ref={el => this.dynamicContentElement = el}
                  dangerouslySetInnerHTML={this.getDynamicContent()}
                  />
                  </div>
                  );
                  }
                  }


                  ReactDOM.render(
                  <Element />,
                  document.getElementById('root')
                  );

                  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
                  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
                  <div id="root"></div>






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 11 at 10:32









                  slawekkolodziej

                  52438




                  52438
























                      up vote
                      0
                      down vote













                      Whenever there is a state change, React removes the html and replaces it.



                      This code will only execute when the component is first loaded, not when state changes and html elements (and their listeners) are wiped:



                      componentDidMount() {
                      document.getElementById('capo').addEventListener('click', this.changeKey);
                      }


                      But this code will run every time the component is rerendered (so whenever a new html element is made, a new listener is attached to it):



                      componentDidUpdate() {
                      document.getElementById('capo').addEventListener('click', this.changeKey);
                      }





                      share|improve this answer

























                        up vote
                        0
                        down vote













                        Whenever there is a state change, React removes the html and replaces it.



                        This code will only execute when the component is first loaded, not when state changes and html elements (and their listeners) are wiped:



                        componentDidMount() {
                        document.getElementById('capo').addEventListener('click', this.changeKey);
                        }


                        But this code will run every time the component is rerendered (so whenever a new html element is made, a new listener is attached to it):



                        componentDidUpdate() {
                        document.getElementById('capo').addEventListener('click', this.changeKey);
                        }





                        share|improve this answer























                          up vote
                          0
                          down vote










                          up vote
                          0
                          down vote









                          Whenever there is a state change, React removes the html and replaces it.



                          This code will only execute when the component is first loaded, not when state changes and html elements (and their listeners) are wiped:



                          componentDidMount() {
                          document.getElementById('capo').addEventListener('click', this.changeKey);
                          }


                          But this code will run every time the component is rerendered (so whenever a new html element is made, a new listener is attached to it):



                          componentDidUpdate() {
                          document.getElementById('capo').addEventListener('click', this.changeKey);
                          }





                          share|improve this answer












                          Whenever there is a state change, React removes the html and replaces it.



                          This code will only execute when the component is first loaded, not when state changes and html elements (and their listeners) are wiped:



                          componentDidMount() {
                          document.getElementById('capo').addEventListener('click', this.changeKey);
                          }


                          But this code will run every time the component is rerendered (so whenever a new html element is made, a new listener is attached to it):



                          componentDidUpdate() {
                          document.getElementById('capo').addEventListener('click', this.changeKey);
                          }






                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Nov 12 at 6:08









                          Mirror318

                          4,84922941




                          4,84922941






























                               

                              draft saved


                              draft discarded



















































                               


                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53246760%2freact-add-a-click-listener-to-dangerouslysetinnerhtml-sub-element%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              Xamarin.iOS Cant Deploy on Iphone

                              Glorious Revolution

                              Dulmage-Mendelsohn matrix decomposition in Python