使用Summernote向隐藏Tab的textarea追加内容时遇appendChild错误求助
首先,我的导航标签页结构如下:
<ul class="nav nav-tabs" role="tablist"> <li role="presentation" ng-click="selectTab(1)"> <a href="javascript:void(0);" ng-class="{active:isActive(1)}">Tab1</a> </li> <li role="presentation" ng-click="selectTab(1)"> <a href="javascript:void(0);" ng-class="{active:isActive(1)}">Tab2</a> </li> </ul>
当我尝试向第二个隐藏div中的textarea字段追加内容时,出现了以下错误:
Error: Failed to execute 'appendChild' on 'Node': Only one element on document allowed.
相关的HTML结构如下:
<div ng-show="isActive(1)"> <!-- true --> <form class="formStep1" name="formStep1"> <input type="number" ng-model="salary" id="salary" name="salary" > </form> </div> <div ng-show="isActive(2)"> <!-- false --> <form name="formStep2" class="formStep2"> <textarea name="job_description" ng-model="job_description" id="job_description" ></textarea> </form> </div>
注: 将textarea放在第一个div的表单中时可正常工作,但我需要将该字段放在第二个标签页的表单中。
我通过以下代码使用Summernote编辑器追加内容,请问该如何调试此问题?
var jobDescription = finTempObj.job_description; //data $('#job_description').summernote('reset'); $('#job_description').summernote('editor.pasteHTML', jobDescription);
调试与解决思路
我来帮你一步步排查这个问题:
先修正标签页点击事件的低级错误
你看导航栏里第二个<li>的ng-click绑定的是selectTab(1),这明显写错了!应该改成selectTab(2),不然第二个标签永远无法激活,对应的div会一直处于ng-show="false"的隐藏状态——而Summernote在操作一个未被渲染到DOM中的元素时,大概率会抛出这类节点操作错误。先把这个问题改了:<li role="presentation" ng-click="selectTab(2)"> <a href="javascript:void(0);" ng-class="{active:isActive(2)}">Tab2</a> </li>确保Summernote在元素可见后再初始化
如果你的Summernote是页面加载时就全局初始化所有textarea,那隐藏的textarea对应的编辑器实例可能根本没正确创建(因为ng-show会把未激活的元素从DOM中移除)。建议改成切换到目标标签时再初始化编辑器,比如在selectTab方法里加判断:$scope.selectTab = function(tabNum) { $scope.activeTab = tabNum; // 切换到Tab2时再初始化Summernote if(tabNum === 2) { // 给Angular一点渲染时间 setTimeout(() => { const $textarea = $('#job_description'); if(!$textarea.hasClass('summernote-inited')) { $textarea.summernote({ // 你的编辑器配置项 }).addClass('summernote-inited'); } }, 100); } }操作编辑器前先确认元素状态
即使标签切换正常,Angular的ng-show渲染偶尔会有延迟,你可以在调用Summernote方法前,先确保目标元素可见且已存在于DOM中:var jobDescription = finTempObj.job_description; // 先切换到Tab2(如果当前没在的话) if(!$scope.isActive(2)) { $scope.selectTab(2); } // 等待DOM渲染完成后再操作 setTimeout(() => { const $textarea = $('#job_description'); if($textarea.is(':visible')) { $textarea.summernote('reset'); $textarea.summernote('editor.pasteHTML', jobDescription); } }, 150);改用Angular集成版Summernote(更优雅的方案)
直接用jQuery操作Angular管理的DOM元素很容易踩异步渲染的坑,推荐使用专门的Angular-Summernote指令,它会自动和Angular的生命周期同步,从根源上避免这类手动DOM操作的问题。
内容的提问来源于stack exchange,提问作者Mr world wide

