Files
wordpress-theme-aksrt/assets/js/front-submit-placeholder.js
T
kate522andClaude Opus 5 44a03fd5f4 fix(submit): render the configured body placeholder in TinyMCE
WordPress 打包的 TinyMCE 4.9 不含官方 placeholder 插件,tinymce.init 里的
placeholder 配置被静默忽略,投稿页「投稿内容占位符」从未显示过。

新增主题插件 assets/js/front-submit-placeholder.js:正文为空时插入一个
data-mce-bogus 的占位段落,有内容即移除;序列化 / 解析都剔除该段落,
字数统计也跳过它,提示文字不会写进文章。

- 经典投稿表单与创作者工作台都通过 external_plugins 加载
  (WordPress 会丢弃名字已在 plugins 列表里的外部插件)
- 占位样式写进 iframe 的 content CSS:submit-editor.css 与
  editor-content-style.ts

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-09 19:01:28 +08:00

135 lines
4.1 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* TinyMCE 正文占位符插件。
*
* WordPress 打包的 TinyMCE 4.9 不含官方 `placeholder` 插件,`tinymce.init` 里的
* `placeholder` 配置会被静默忽略——投稿页的「投稿内容占位符」因此从来没有显示过。
* 这里用官方插件同样的机制补上:正文为空时插入一个不可编辑的占位段落,
* 有内容时移除。
*
* 占位段落带 `data-mce-bogus="all"`,TinyMCE 不参与序列化、字数和选区计算;
* 视觉样式写在 content CSS(submit-editor.css / editor-content-style.ts)的
* `.jio-editor-placeholder` 上——编辑器正文在 iframe 里,页面样式管不到。
*
* @package Jio
*/
(function (window) {
'use strict';
if (!window.tinymce || !window.tinymce.PluginManager) {
return;
}
var ATTR = 'data-jio-placeholder';
var MEDIA = 'img,iframe,table,pre,hr,video,audio,figure,.jio-math--block';
window.tinymce.PluginManager.add('jio_placeholder', function (editor) {
var text = String(editor.getParam('placeholder', '') || '').trim();
if (!text) {
return;
}
function body() {
return editor.getBody();
}
function nodes() {
var root = body();
return root ? root.querySelectorAll('[' + ATTR + ']') : [];
}
/** 正文字数:跳过占位段落自身的文字。 */
function contentText() {
var root = body();
if (!root) {
return '';
}
var clone = root.cloneNode(true);
Array.prototype.forEach.call(clone.querySelectorAll('[' + ATTR + ']'), function (node) {
node.parentNode && node.parentNode.removeChild(node);
});
return String(clone.textContent || '');
}
function isEmpty() {
var root = body();
if (!root) {
return false;
}
// 图片 / 视频 / 表格这类块即使没有文字也算有内容。
if (root.querySelector(MEDIA)) {
return false;
}
return contentText().replace(/[\s ]+/g, '') === '';
}
function hide() {
Array.prototype.forEach.call(nodes(), function (node) {
node.parentNode && node.parentNode.removeChild(node);
});
}
function show() {
var root = body();
if (!root || root.querySelector('[' + ATTR + ']') || !isEmpty()) {
return;
}
var paragraph = editor.dom.create('p', {
'class': 'jio-editor-placeholder',
'contenteditable': 'false',
'data-mce-bogus': 'all',
'data-jio-placeholder': '1',
}, editor.dom.encode(text));
root.insertBefore(paragraph, root.firstChild);
}
function sync() {
if (isEmpty()) {
show();
} else {
hide();
}
}
// 占位段落不进入文章内容:序列化时剔除,解析(setContent)时同样剔除,
// 避免把提示文字当成正文保存。
editor.on('PreInit', function () {
var strip = function (list) {
list.forEach(function (node) {
if (node.attr(ATTR)) {
node.remove();
}
});
};
editor.serializer.addNodeFilter('p', strip);
editor.parser.addNodeFilter('p', strip);
});
editor.on('init', sync);
editor.on('SetContent input keyup change Undo Redo NodeChange', function () {
// SetContent 之后 DOM 才稳定,放到下一帧再判断。
window.setTimeout(sync, 0);
});
// 正文里只有占位段落时,光标没有可落点:补一个空段落并把光标放进去。
var ensureCaret = function () {
var root = body();
if (!root) {
return;
}
// 占位段落 pointer-events:none,点击会落到 body 本身;光标已经落在
// 段落里时不要再动它,避免每次点击都挪动光标。
var node = editor.selection.getNode();
if (node && node.nodeType === 1 && node !== root && node.nodeName !== 'BODY') {
return;
}
hide();
if (!root.firstChild) {
root.appendChild(editor.dom.create('p', {}, '<br data-mce-bogus="1">'));
}
editor.selection.setCursorLocation(root.firstChild, 0);
};
editor.on('click', ensureCaret);
});
})(window);