[Edit:我也尝试了使用错误的SetInnerHTML代码,解决了validateDOMNesting问题,但仍然没有交互性,我猜JavaScript代码被禁用或不工作]
所以我从后端获取一个文件,并在我的React应用程序中呈现它。我正在获取的文件是游戏文件,如井字游戏,石头剪刀布,等等,所有这些游戏都是用html和js构建的。问题是,当我渲染html文件时,处理程序不工作,例如在井字游戏中,我有处理点击的函数,在用户点击一个点后,它的innerHtml被设置为X或O,但这些处理程序不工作,并且发生错误:我在下面附上代码和截图
default function RenderGame({ game }) {
return (
<>
<div>
{ReactHtmlParser(game)}
</div>
</>
)
}
const PlayGame = ({ temp }) => {
const [game, setGame] = useState({})
const params = useParams()
const gameID = <params className="id"></params>
useQuery(`project-${gameID}`, async () => {
const resp = await axios.get(`${import.meta.env.VITE_API_URL}/projects/${gameID}/iframe`)
console.log(resp)
return resp.data
}, {
onSuccess: (data) => {
setGame(data)
}
})
const handleDestroy = (e) => {
e.preventDefault()
deleteGame.mutate()
}
return (
<RenderGame game={game}></RenderGame>
)
}
export default PlayGame;
游戏的代码如下:
"<!-- app/views/projects/iframe.html.erb -->
<!DOCTYPE html>
<html>
<head>
<!-- Add the base tag here only if there are image files -->
<base href="/uploads/image_folder_1693815923/">
</head>
<body>
<!-- Render the HTML content -->
<div class="message-code">
<!DOCTYPE html>
<html>
<head>
<style>
*{
margin: 0;
padding: 0;
background-color: black
}
.title{
text-align: center;
margin-bottom: 0px;
color: #808080;
font-style: italic;
font-weight: bold;
}
.gameContainer{
display: flex;
justify-content: center;
padding-top: 20px;
}
.container{
display: grid;
grid-template-rows: repeat(3, 10vw);
grid-template-columns: repeat(3, 10vw);
position: relative;
justify-content: center;
}
.box{
border: 2px solid #C0C0C0;
font-size: 6vw;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
}
.box:hover{
background-color: #191919;
}
.info {
font-size: 28px;
color: #A0A0A0;
text-align: center;
font-style: italic;
}
.player {
text-align: center;
}
.gameInfo{
padding: 30px 34px;
text-align: center;
}
.btn{
margin-top: 4px;
cursor: pointer;
}
.line{
background-color: black;
height: 3px;
width: 0;
position: absolute;
background-color: #911d91;
transition: width 0.3s ease-in-out;
text-align:center;
}
.boxtext{
color: white;
background-color: transparent;
}
</style>
</head>
<body>
<h1 class="title">Welcome To Tic Tac Toe</h1>
<div class="player">
<span class="info">Turn for: X</span>
</div>
<div class="gameContainer">
<div class="container">
<div class="line"></div>
<div class="box border-top-0 border-left-0"><span class="boxtext"></span></div>
<div class="box border-top-0"><span class="boxtext"></span></div>
<div class="box border-top-0 border-right-0"><span class="boxtext"></span></div>
<div class="box border-left-0"><span class="boxtext"></span></div>
<div class="box"><span class="boxtext"></span></div>
<div class="box border-right-0"><span class="boxtext"></span></div>
<div class="box border-bottom-0 border-left-0"><span class="boxtext"></span></div>
<div class="box border-bottom-0"><span class="boxtext"></span></div>
<div class="box border-bottom-0 border-right-0"><span class="boxtext"></span></div>
</div>
</div>
<div class="gameInfo">
<button id="reset" class="btn btn-secondary btn-lg">Start New Game</button>
</div>
<script>
function initializetictactoe(){
let turn = "X"
let isgameover = false;
const changeTurn = ()=>{
return turn === "X"? "0": "X"
}
const checkWin = ()=>{
let boxtext = document.getElementsByClassName('boxtext');
let wins = [
[0, 1, 2, 5, 5, 0],
[3, 4, 5, 5, 15, 0],
[6, 7, 8, 5, 25, 0],
[0, 3, 6, -5, 15, 90],
[1, 4, 7, 5, 15, 90],
[2, 5, 8, 15, 15, 90],
[0, 4, 8, 5, 15, 45],
[2, 4, 6, 5, 15, 135],
]
wins.forEach(e =>{
if((boxtext[e[0]].innerText === boxtext[e[1]].innerText) && (boxtext[e[2]].innerText === boxtext[e[1]].innerText) && (boxtext[e[0]].innerText !== "") ){
document.querySelector('.info').innerText = boxtext[e[0]].innerText + " Won"
isgameover = true
document.querySelector(".line").style.transform = `translate(${e[3]+29}vw, ${e[4]}vw) rotate(${e[5]}deg)`
document.querySelector(".line").style.width = "20vw";
setTimeout(function() {
if (confirm(turn+' Lose! Play Again?')) {
location.reload();
} else {
window.location.href = '<%= user_path(current_user.id) %>';
}
}, 400);
}
})
}
let boxes = document.getElementsByClassName("box");
Array.from(boxes).forEach(element =>{
let boxtext = element.querySelector('.boxtext');
element.addEventListener('click', ()=>{
if(boxtext.innerText === ''){
boxtext.innerText = turn;
turn = changeTurn();
checkWin();
if (!isgameover){
document.getElementsByClassName("info")[0].innerHTML = "Turn for<strong>: " + turn + "</strong>";
}
}
})
})
reset.addEventListener('click', ()=>{
let boxtexts = document.querySelectorAll('.boxtext');
Array.from(boxtexts).forEach(element => {
element.innerText = ""
});
turn = "X";
isgameover = false
document.querySelector(".line").style.width = "0vw";
document.getElementsByClassName("info")[0].innerText = "Turn for " + turn;
})
}
initializetictactoe();
</script>
</body>
</div>
</body>
</html>
"
1条答案
按热度按时间guykilcj1#
我认为你可以再次尝试使用SetInnerHTML,但这一次,尝试使用
srcdoc
(而不是src
)属性将游戏HTML放入<iframe>
中。srcdoc
属性让您指定要在<iframe>
中显示的HTML。下面是我做的一个CodePen演示用法(如果它一开始没有正确加载,用Ctrl+Shift+7重新加载代码。我不知道它为什么会这样做,但当我在桌面上尝试本地React项目中的代码时,它运行得很好):https://codepen.io/SonicBoomNFA/pen/eYbbQLy
getGame()
模拟检索游戏的HTML。它返回测试两个事件侦听器的普通HTML(一个使用onclick
属性直接在黄色正方形上,另一个使用addEventListener()
在<body>
下面使用<script>
标记)。我还对字符串调用sanitize()
,它将游戏HTML中的所有双引号替换为"
,因为嵌套的双引号会破坏代码。<iframe>
上的onload
属性调整iframe的大小以适应其内容(受此Adjust width and height of iframe to fit with content in it的启发)。