fix(submit): let the editor iframe grow with the body instead of scrolling internally

正文超过 iframe 高度时会在编辑器内部出现第二根滚动条,正文应该随页面一起变长。
登录态实测确认了滚动来源与两个副作用:

1. 经典投稿表单完全没做高度同步(iframe 固定 504px,正文 1824px → 内滚动)。
   在共用基础插件里补上 syncHeight():测量正文高度并同步 iframe,
   下限取编辑器声明高度;图片加载后重新同步。工作台自带 syncEditorHeight()
   负责块浮层定位,用 __jioStudioOwnsEditorHeight 让插件跳过,避免双写。

2. 直接量 scrollHeight 会把 body 的 min-height 撑满算进内容高度,形成
   「撑高 → 再撑高」的反馈循环(实测涨到 22616px)。测量时临时把
   min-height 归零、同帧恢复。

3. body 的 min-height 基准从 100% 改成 100vh:iframe 随正文变高,
   百分比会跟着循环;同时把 WordPress content CSS 的 body 外边距改内边距,
   外边距不计入高度,会让 html 比 iframe 高一截、长正文时又冒出内滚动。

登录态实测(studio 与经典两条路径):
- iframe 2152px / 1874px,与正文一致,innerHasScrollbar=false;
- 空正文占位符正常、点击空白落光标正常;
- 只有页面级滚动。

测试:新增 iframe 随正文增长、测量忽略 min-height 两个回归用例;903 全通过。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kate522andClaude Opus 5 committed 2026-11-09 21:37:14 +08:00
1 parent 305896796c
commit 4f9347b6aa
7 files changed
+164 -38

No files matched your search

+58 -1
View File
@@ -15,6 +15,11 @@
* 能把光标移回正文;正文以表格、代码块这类结构块结尾时更是无处可落。这里
* 在点击空白处时补一个可编辑尾段并把光标放进去。
*
* 3) 高度跟随正文
* TinyMCE 的 iframe 是固定高度,正文超过它就在编辑器内部出现滚动条——正文
* 应该随页面一起变长,而不是套一层内滚动。这里测量正文高度并同步 iframe,
* 下限取编辑器声明的高度(写作区不会缩成一行)。
*
* 视觉样式写在 content CSS(submit-editor.css / editor-content-style.ts)——
* 编辑器正文在 iframe 里,页面样式管不到。
*
@@ -127,11 +132,63 @@
editor.selection.setCursorLocation(home, home.childNodes.length);
}
/**
* 让 iframe 高度跟随正文增长,避免编辑器内部出现第二根滚动条。
*
* 下限取编辑器声明的高度:正文很短时写作区也不会缩成一行。
*
* 创作者工作台自带一套高度同步(editor-runtime.ts 的 syncEditorHeight,
* 还负责块浮层定位),那边由它接管,这里不重复计算。
*/
function syncHeight() {
if (window.__jioStudioOwnsEditorHeight) {
return;
}
var body = root();
var doc = editor.getDoc();
if (!body || !doc) {
return;
}
var container = editor.getContentAreaContainer && editor.getContentAreaContainer();
var iframe = container ? container.querySelector('iframe') : null;
if (!iframe) {
return;
}
// body 的 min-height 让空正文铺满 iframe;直接量 scrollHeight 会把它算进
// 内容高度,得到「撑高 → 再撑高」的反馈循环(实测会涨到两万多 px)。
// 测量时临时归零,同一帧内恢复,不会产生可见闪烁。
var prevMinHeight = body.style.minHeight;
body.style.minHeight = '0';
var contentHeight = Math.max(body.scrollHeight || 0, body.offsetHeight || 0);
body.style.minHeight = prevMinHeight;
var floor = Math.max(320, Number(editor.getParam('height', 0)) || 0);
var height = Math.max(floor, contentHeight);
// 只在变化时写回,避免每次按键都触发一次布局。
if (iframe.style.height !== height + 'px') {
iframe.style.height = height + 'px';
}
iframe.style.overflow = 'hidden';
iframe.setAttribute('scrolling', 'no');
}
// PreInit 时 body 可能已就绪:先打上属性,避免首帧闪一下空白再出现提示。
editor.on('PreInit init', syncPlaceholder);
editor.on('SetContent input keyup change Undo Redo NodeChange', function () {
// SetContent 之后 DOM 才稳定,放到下一帧再判断。
window.setTimeout(syncPlaceholder, 0);
window.setTimeout(function () {
syncPlaceholder();
syncHeight();
}, 0);
});
// 图片 / 视频加载完成后正文高度会变,需要重新同步。
editor.on('init', function () {
var body = root();
if (body) {
body.addEventListener('load', syncHeight, true);
}
syncHeight();
});
editor.on('click', restoreCaret);