繁体   English   中英

如何将 onclick 事件添加到 reactjs 中 dangerouslysetInnerHtml 呈现的字符串中?

[英]How to add onclick event to a string rendered by dangerouslysetInnerHtml in reactjs?

我有一个字符串,即

let string= "Hello <b>Click here</b>";

render() {
return (<div dangerouslySetInnerHTML={this.createMarkup(value)}/>
}

createMarkup = value => { 
        return { __html: value };
 };

我想要的是能够将 onclick 事件添加到<b>标签,在点击时执行 state 操作。

潜在的问题是我有一个 function,它应该呈现 API 传递的任何内容。API 将发送一个字符串“收到订单 ID 123的款项”,或者可以是我无法控制的任何字符串。 后来接到一个需求,加粗的item必须是可以点击的,才能进行一些操作。 我没有任何其他方法可以解决它。

我怎样才能做到这一点?

警告:这听起来像是一个X/Y 问题,其中底层问题(无论是什么)应该以不同的方式解决,这样您就不必向通过dangerouslySetInnerHTML SetInnerHTML 创建的 DOM 元素添加点击处理程序(理想情况下,这样您就不必根本不必通过dangerouslySetInnerHTML创建 DOM 元素)。 但是回答您提出的问题:(您已经阐明了用例;下面的解决方案 #1 适用并且不是不好的做法。)

我不认为你可以直接做到这一点。 我能想到的两种解决方案:

  1. 使用委派的事件处理程序的div :添加点击处理程序上的div ,但后来只有在点击通过通过采取行动b元素。

  2. div上使用ref ,然后在componentDidMountcomponentDidUpdate连接点击处理程序(通过querySelector或类似方法在div找到b元素),大致如下:

这是#1的示例:

<div onClick={this.clickHandler} dangerouslySetInnerHTML={this.createMarkup(string)}/>

... clickHandler在哪里

clickHandler(e) {
    // `target` is the element the click was on (the div we hooked or an element
    // with in it), `currentTarget` is the div we hooked the event on
    const el = e.target.closest("B");
    if (el && e.currentTarget.contains(el)) {
        // ...do your state change...
    }
}

...或者如果您需要支持没有ParentNode#closest旧浏览器:

clickHandler(e) {
    // `target` is the element the click was on (the div we hooked or an element
    // with in it), `currentTarget` is the div we hooked the event on
    let el = e.target;
    while (el && el !== e.currentTarget && el.tagName !== "B") {
        el = el.parentNode;
    }
    if (el && el.tagName === "B") {
        // ...do your state change...
    }
}

...以及在构造函数中绑定clickHandler的位置(而不是使用带有箭头函数的属性;为什么: 1 , 2 ):

this.clickHandler = this.clickHandler.bind(this);

现场示例:

 let string = "Hello <b>Click here</b>"; class Example extends React.Component { constructor(props) { super(props); this.state = { clicks: 0 }; this.clickHandler = this.clickHandler.bind(this); } clickHandler(e) { // `target` is the element the click was on (the div we hooked or an element // with in it), `currentTarget` is the div we hooked the event on // Version supporting older browsers: let el = e.target; while (el && el !== e.currentTarget && el.tagName !== "B") { el = el.parentNode; } if (el && el.tagName === "B") { this.setState(({clicks}) => ({clicks: clicks + 1})); } // Alternative for modern browsers: /* const el = e.target.closest("B"); if (el && e.currentTarget.contains(el)) { this.setState(({clicks}) => ({clicks: clicks + 1})); } */ } createMarkup = value => { return { __html: value }; }; render() { const {clicks} = this.state; return [ <div>Clicks: {clicks}</div>, <div onClick={this.clickHandler} dangerouslySetInnerHTML={this.createMarkup(string)}/> ]; } } ReactDOM.render( <Example />, document.getElementById("root") );
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

这是 #2 的示例,但如果 A) 您可以单独解决潜在问题,或者 B) #1 有效,则不要这样做:

 let string = "Hello <b>Click here</b>"; class Example extends React.Component { constructor(props) { super(props); this.state = { clicks: 0 }; this.divRef = React.createRef(); this.hooked = null; this.clickHandler = this.clickHandler.bind(this); } clickHandler() { this.setState(({clicks}) => ({clicks: clicks + 1})); } hookDivContents() { // Get the b element const b = this.divRef.current && this.divRef.current.querySelector("b"); // No-op if it's not there or it's the same element we have hooked if (!b || b === this.hooked) { return; } // Unhook the old, hook the new if (this.hooked) { this.hooked.removeEventListener("click", this.clickHandler); } this.hooked = this.divRef.current; this.hooked.addEventListener("click", this.clickHandler); } componentDidMount() { this.hookDivContents(); } componentDidUpdate() { this.hookDivContents(); } createMarkup = value => { return { __html: value }; }; render() { const {clicks} = this.state; return [ <div>Clicks: {clicks}</div>, <div ref={this.divRef} dangerouslySetInnerHTML={this.createMarkup(string)}/> ]; } } ReactDOM.render( <Example />, document.getElementById("root") );
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Refs是一个“逃生舱”,让您可以直接访问 DOM。 不要轻易使用 refs; 通常,有更好的选择。

但同样:我会以不同的方式解决根本问题,无论它是什么。

react-html-parser可以将 HTML 字符串转换为 React 组件。

使用转换回调函数,您可以使用 JSX 标记更新 HTML 字符串中的任何标记,添加任何属性和事件侦听器。

这是我使用它的方式:

ReactHtmlParser(item.value, {
    transform: (node) => {
        if (node.name === 'a' && node.attribs && node.attribs.href) {
            const matched = node.attribs.href.match(/^activity\/([0-9]+)$/i);

            if (matched && matched[1]) { // activity id
                return <a
                        href={node.attribs.href}
                        onClick={(e) => {                                                            
                          e.preventDefault();                                                            
                          this.props.openActivityModal(matched[1]);
                        }}
                        >{node.children[0].data}</a>
            }
        }
    } 
})

这是满足您需求的干净方法。 通过根据<br>标签拆分字符串,您最终可以得到一个可映射的文本数组:

 class BoldText extends React.Component { constructor(props) { super(props) this.state = { input: "Money received for order ID <b>123</b>, wow for real, so <b>cool</b> its insane" } } boldClick = ev => { console.log('clicked !') } render() { const { input } = this.state const a = input.split('</b>') const filter = /<b>.*<\\/b>/ const text = input.split(filter) const clickable = filter.exec(input) //<b onClick={this.boldClick}></b> return ( <div> <p>{a.map(t => { const [text, bold] = t.split('<b>') return <span>{text}<b onClick={this.boldClick}>{bold}</b></span> })} </p> </div> ) } } ReactDOM.render(<BoldText />, document.getElementById('root'))
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.production.min.js"></script> <idv id='root'>

此解决方案应该可以解决您在上述答案的评论中提到的问题。 您可以将 API 调用放在componentDidMount生命周期函数中并从那里更改状态。

您可以将父标签设为<form>并设置onClick="SomeFunction()"

从具有 HTML 字符串的子标记中,设置type="button"

let string= "Hello <b type='button'>Click here</b>";

render() {
return (
     <form onClick={SomeFunction} dangerouslySetInnerHTML = 
      {this.createMarkup(value)}/>
}

createMarkup = value => { 
    return { __html: value };
};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM