如何将 onclick 事件添加到 reactjs 中由 dangerouslysetInnerHtml 呈现的字符串? [英] How to add onclick event to a string rendered by dangerouslysetInnerHtml in reactjs?

查看:53
本文介绍了如何将 onclick 事件添加到 reactjs 中由 dangerouslysetInnerHtml 呈现的字符串?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个字符串,即

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

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

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

我想要的是能够向 <b> 标签添加一个 onclick 事件,以便在点击时执行状态操作.

What I would like is to be able to add an onclick event to the <b> tag perform state manipulations on click.

根本问题是我有一个函数应该渲染 API 传递的任何内容.API 会发送一个字符串'Money received for order ID 123',或者可以是我无法控制的任何字符串.后来,我得到了一个要求,加粗的项目必须是可点击的,以便执行一些操作.我没有其他办法解决.

The underlying problem is where I had a function which was supposed to render whatever is passed by the API. The API would send a string 'Money received for order ID 123', or could be any string that I have no control over. Later, I got a requirement where the item that is bolded must be clickable, so as to perform some actions. I didn't have any other way to solve it.

我怎样才能做到这一点?

How can I achieve this?

推荐答案

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

Caveat: This sounds like an X/Y problem, where the underlying problem (whatever it is) should be solved differently, so that you don't have to add a click handler to a DOM element created via dangerouslySetInnerHTML (ideally, so you don't have to create DOM elements via dangerouslySetInnerHTML at all). But answering the question you asked: (You've clarified the use case; solution #1 below applies and isn't poor practice.)

我认为你不能直接这样做.我能想到的两个解决方案:

I don't think you can do that directly. Two solutions I can think of:

  1. div 上使用委托事件处理程序:在 div 上添加点击处理程序,但只有在点击通过 时才执行操作b 元素.

  1. Use delegated event handler on the div: Add a click handler on the div, but then only take action if the click passed through the b element.

div 上使用 ref,然后在 componentDidMountcomponentDidUpdate(通过 querySelector 或类似方法在 div 中查找 b 元素),类似以下内容:

Use a ref on the div, and then hook the click handler up in componentDidMount and componentDidUpdate (finding the b element within the div via querySelector or similar), something along these lines:

这是#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 的旧版浏览器:

...or if you need to support older browsers without 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):

...and where you bind clickHandler in the constructor (rather than using a property with an arrow function; why: 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 有效,则不要这样做:

Here's an example of #2, but don't do this if A) You can solve the underlying problem separately, or B) #1 works:

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;通常,会有更好的选择.

Refs are an "escape hatch" giving you direct DOM access. Don't use refs lightly; usually, there's a better choice.

但又一次:我会以不同的方式解决根本问题.

But again: I would solve the underlying problem, whatever it is, differently.

这篇关于如何将 onclick 事件添加到 reactjs 中由 dangerouslysetInnerHtml 呈现的字符串?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆