英文:
Error question when using recursive function in JavaScript
问题
在'findChildComment'方法的最后一行,“document.querySelector(idForInnerHtml).innerHTML = commentHtml;”处发生“Uncaught TypeError: Cannot set properties of null (setting 'innerHTML')”错误。
我通过日志检查发现“<id='cm_reply_view${row.id}'>”和“<var idForInnerHtml = '#cm_reply_view' + parentId;>”具有相同的值,但我不知道哪个部分有问题。如果有人知道,请告诉我,谢谢。
请注意,此错误表明在尝试设置HTML内容时,找不到指定的DOM元素。可能的原因是DOM元素不存在或JavaScript代码在尝试访问DOM元素之前运行。
要解决此问题,您可以采取以下步骤:
-
确保HTML中存在具有相应ID的DOM元素,例如“cm_reply_view${row.id}”。
-
确保JavaScript代码在尝试访问这些DOM元素之前,页面已经加载完毕。您可以将代码包装在“window.onload”事件处理程序中,以确保在页面完全加载后运行。
-
在代码中添加适当的错误处理,以处理DOM元素不存在的情况,例如使用条件语句检查元素是否存在,然后执行操作。
请根据上述建议检查您的代码,并确保DOM元素存在且JavaScript代码在需要时运行。这应该有助于解决您遇到的问题。
英文:
I'm implementing a bulletin board comment function, but I'm blocked from looking up comments.
I made a recursive function to make the big comments appear under the comments, but I keep getting errors, but I don't know where the problem is.
window.onload = () => {
findAllComment();
}
// 전체 댓글 조회
function findAllComment() {
const postId = [[ ${post.id} ]];
const accountId = [[ ${account.id} ]]
$.ajax({
url: `/api/posts/${postId}/comments`,
beforeSend: function (xhr) {
xhr.setRequestHeader(header, token);
},
type: 'get',
dataType: 'json',
async: false,
success: function (response) {
console.log(response);
// 1. 조회된 데이터가 없는 경우
if (!response.result.data.length) {
return false;
}
// 2. 렌더링 할 HTML을 저장할 변수
let commentHtml = '';
// 3. 댓글 HTML 추가
response.result.data.forEach(row => {
commentHtml += `
<div class="card py-0 mt-3">
<div class="card-header bg-light">
<span class="comment-writer"><span class="card-text">${row.account.nickname}</span></span>
<a sec:authorize="isAuthenticated()" th:if="${row.account.id == accountId}">
<a class="comment-delete" onclick="deleteComment(${row.id});"><i class="bi bi-trash3"></i><span class="card-text ml-1">삭제</span></a>
<a class="comment-edit" onclick="findEditComment(${row.id});"><i class="fa fa-pencil"></i><span class="card-text ml-1">수정</span></a>
</a>
<a class="comment-reply" onclick="findReplyComment(${row.id})"><i class="fa fa-comment fa"></i><span class="card-text ml-1">답글</span></a>
<span class="date" id="commentDate">${dayjs(row.createdDateTime).format('YYYY-MM-DD HH:mm')}</span>
</div>
<div class="card-body">
<span class="comment-content"><span class="card-text">${row.content}</span></span>
</div>
</div>
<!--/* 답글 뷰 */-->
<div id="cm_reply_view${row.id}"></div>
<!--/* 댓글 수정 뷰 */-->
<div id="cm_edit_view${row.id}"></div>
<!--/* 댓글 답글 작성 뷰 */-->
<div id="cm_createReply_view${row.id}"></div>
`;
if (row.children.length) {
row.children.forEach(childRow => {
findChildComment(row.id, childRow);
})
}
})
// 4. class가 "cm_list"인 요소를 찾아 HTML을 렌더링
document.querySelector('.cm_list').innerHTML = commentHtml;
},
error: function (request, status, error) {
console.log(error)
}
})
}
// 답글 조회
function findChildComment(parentId, child) {
var idForInnerHtml = '#cm_reply_view' + parentId;
const accountId = [[ ${account.id} ]];
let commentHtml = `
<div class="card py-0 mt-3">
<div class="card-header bg-light">
<span class="comment-writer"><span class="card-text">${child.account.nickname}</span></span>
<a sec:authorize="isAuthenticated()" th:if="${child.account.id == accountId}">
<a class="comment-delete" onclick="deleteComment(${child.id});"><i class="bi bi-trash3"></i><span class="card-text ml-1">삭제</span></a>
<a class="comment-edit" onclick="findEditComment(${child.id});"><i class="fa fa-pencil"></i><span class="card-text ml-1">수정</span></a>
</a>
<a class="comment-reply" onclick="findReplyComment(${child.id})"><i class="fa fa-comment fa"></i><span class="card-text ml-1">답글</span></a>
<span class="date" id="commentDate">${dayjs(child.createdDateTime).format('YYYY-MM-DD HH:mm')}</span>
</div>
<div class="card-body">
<span class="comment-content"><span class="card-text">${child.content}</span></span>
</div>
</div>
<!--/* 답글 뷰 */-->
<div id="cm_reply_view${child.id}"></div>
<!--/* 댓글 수정 뷰 */-->
<div id="cm_edit_view${child.id}"></div>
<!--/* 댓글 답글 작성 뷰 */-->
<div id="cm_createReply_view${child.id}"></div>
`;
if (child.children.length) {
child.children.forEach(childRow => {
findChildComment(child.id, childRow);
})
}
document.querySelector(idForInnerHtml).innerHTML = commentHtml;
}
"Uncaught TypeError: Cannot set properties of null (setting 'innerHTML')" error occurs at "document.querySelector(idForInnerHtml).innerHTML = commentHtml;" last line of 'findChildComment ' method.
I checked through logs that <id="cm_reply_view${row.id}"> and <var idForInnerHtml = '#cm_reply_view' + parentId;> has same value, but I don't know which part is the problem. If anyone knows, please let me know, thank you.
答案1
得分: 2
问题在于您在执行递归之后才分配HTML。然而,递归调用需要父div
的HTML已经存在于文档中。因此,您需要稍微调整逻辑。
以下是一些对代码的更改,应该可以使其正常工作:
-
在
success
回调中,第3步,在进行递归调用之前,首先完成forEach
循环:response.result.data.forEach(row => { commentHtml += /* 这里放入您的HTML */; }); // 首先完成顶部的HTML,然后将收集到的HTML加载到文档中 document.querySelector('.cm_list').innerHTML = commentHtml; // 现在才执行递归,这需要HTML已经存在于文档中。 response.result.data.forEach(row => { if (row.children.length) { row.children.forEach(childRow => { findChildComment(row.id, childRow); }) } })
-
在
findChildComment
中也需要进行类似的更改:let commentHtml = /* 这里放入您的HTML */; // 再次强调:首先分配递归调用所需的HTML document.querySelector(idForInnerHtml).innerHTML = commentHtml; // ...然后进行递归调用: if (child.children.length) { child.children.forEach(childRow => { findChildComment(child.id, childRow); }) }
英文:
The problem is that you are assigning the HTML after having performed the recursion. Yet the recursive calls need the HTML with the parent div
to already be present in the document. So you need to reverse the logic a bit.
Here are some changes to the code that should make it work:
-
In the
success
callback, in step 3, first finish theforEach
loop before making recursive calls:response.result.data.forEach(row => { commentHtml += /* Your HTML Comes here */ ; }); // Finish the top HTML first, then load the collected HTML into the document document.querySelector('.cm_list').innerHTML = commentHtml; // And only now perform the recursion, which needs that HTML // to already be present in the document. response.result.data.forEach(row => { if (row.children.length) { row.children.forEach(childRow => { findChildComment(row.id, childRow); }) } })
-
A similar change is needed in
findChildComment
:let commentHtml = /* Your HTML comes here */; // Again: first assign the HTML that you need for the recursive calls document.querySelector(idForInnerHtml).innerHTML = commentHtml; // ... and then make the recursive calls: if (child.children.length) { child.children.forEach(childRow => { findChildComment(child.id, childRow); }) }
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论