警告:这听起来像是一个X/Y 问题,其中潜在的问题(无论是什么)应该以不同的方式解决,这样您就不必向通过创建的 DOM 元素添加点击处理程序dangerouslySetInnerHTML
(理想情况下,所以您不必根本不必通过创建 DOM 元素dangerouslySetInnerHTML
)。但是回答您提出的问题:( 您已经阐明了用例;下面的解决方案 #1 适用并且不是不好的做法。)
我不认为你可以直接做到这一点。我能想到的两种解决方案:
在 上使用委托事件处理程序:在 上div
添加单击处理程序div
,但仅当单击通过b
元素时才执行操作。
在ref
上使用 a div
,然后将点击处理程序连接到componentDidMount
和componentDidUpdate
(b
在过div
孔querySelector
或类似物内找到元素),沿着以下几行:
以下是#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;通常,有更好的选择。
但再说一次:我会以不同的方式解决根本问题,无论它是什么。