问题描述
我有一个字符串,即
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?
推荐答案
(您已经阐明了用例;下面的解决方案 #1 适用并且不是不好的做法.)
(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:
在
div
上使用委托事件处理程序:在div
上添加点击处理程序,但只有在点击通过时才执行操作b
元素.
Use delegated event handler on the
div
: Add a click handler on thediv
, but then only take action if the click passed through theb
element.
在 div
上使用 ref
,然后在 componentDidMount
和 componentDidUpdate中挂钩点击处理程序code>(通过
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 呈现的字符串?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!