Предостережение: Это звучит как X / Y проблема , где основная проблема (какой бы она ни была) должна решаться по-другому, чтобы у вас не было добавить обработчик кликов к элементу DOM, созданному с помощью dangerouslySetInnerHTML
(в идеале, чтобы вам вообще не приходилось создавать элементы DOM с помощью dangerouslySetInnerHTML
). Но отвечая на вопрос, который вы задали: (Вы разъяснили вариант использования; приведенное ниже решение № 1 применимо и не является плохой практикой.)
Я не думаю, что вы можете сделать это напрямую. Два решения, которые я могу придумать:
Используйте делегированный обработчик событий для div
: добавьте обработчик щелчков для div
, но затем выполняйте действие только в том случае, если щелчок прошел через элемент b
.
Используйте ref
на 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);
Live Пример:
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>
Ссылки - это «аварийный люк», дающий вам прямой доступ к DOM. Не используйте ссылки легкомысленно; как правило, есть лучший выбор.
Но опять же: Я бы решил основную проблему, какой бы она ни была, по-другому.