EP131. “自定义 SVG 菜单图标与仅限单页的 CSS”
🔒 登录后可标记已读两件事:① 把上一讲占位用的内建 dashicons-smiley 换成自己的 SVG 图标——演示了两种做法,作者更推荐「把 SVG 内容转成 Base64 编码直接内嵌进代码」,这样 WordPress 能像处理内建图标一样自动套用标准灰色/悬停高亮蓝色,而不是「指向一个独立的 SVG 文件」(这种做法图标会保留 SVG 原始颜色,不会被 WordPress 重新上色);② 手写 Words List 页面的表单 HTML,并学习怎么只在这一个后台页面加载专属的 CSS 文件,不影响其他后台页面。
涉及文件
wp-content/plugins/our-word-filter-plugin/index.php(修改)wp-content/plugins/our-word-filter-plugin/styles.css(新建)wp-content/plugins/our-word-filter-plugin/custom.svg(新建,图标素材,仅在「指向独立文件」的备选方案里用到)
代码实现
index.php:ourMenu() 里把图标换成 Base64 内嵌 SVG,并挂载仅限本页的 CSS:
function ourMenu() {
$mainPageHook = add_menu_page('Words To Filter', 'Word Filter', 'manage_options', 'ourwordfilter', array($this, 'wordFilterPage'), 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4...(后略,完整字符串是整份 SVG 文件转出来的 Base64 编码)', 100);
add_submenu_page('ourwordfilter', 'Words To Filter', 'Words List', 'manage_options', 'ourwordfilter', array($this, 'wordFilterPage'));
add_submenu_page('ourwordfilter', 'Word Filter Options', 'Options', 'manage_options', 'word-filter-options', array($this, 'optionsSubPage'));
add_action("load-{$mainPageHook}", array($this, 'mainPageAssets'));
}
function mainPageAssets() {
wp_enqueue_style('filterAdminCss', plugin_dir_url(__FILE__) . 'styles.css');
}
图标的另一种写法(指向独立 SVG 文件,会保留原始配色,不被 WordPress 重新上色):
// 备选方案:直接给文件路径,而不是内嵌 Base64
plugin_dir_url(__FILE__) . 'custom.svg'
wordFilterPage():手写表单 HTML:
function wordFilterPage() { ?>
<div class="wrap">
<h1>Word Filter</h1>
<form method="POST">
<label for="plugin_words_to_filter"><p>Enter a <strong>comma-separated</strong> list of words to filter from your site's content.</p></label>
<div class="word-filter__flex-container">
<textarea name="plugin_words_to_filter" id="plugin_words_to_filter" placeholder="bad, mean, awful, horrible"></textarea>
</div>
<input type="submit" name="submit" id="submit" class="button button-primary" value="Save Changes">
</form>
</div>
<?php }
styles.css(新建,只给这一个页面用):
.word-filter__flex-container {
display: flex;
}
.word-filter__flex-container textarea {
flex: 1;
height: 200px;
margin-bottom: 20px;
}
关键改动点:
- Base64 内嵌图标的生成方式:把 SVG 源文件内容复制出来,在浏览器控制台执行
btoa(\...SVG 原始内容...\)(用反引号包裹避免 SVG 里的引号跟 JS 字符串引号冲突),把返回结果(去掉首尾多余的引号)粘贴进data:image/svg+xml;base64,后面——这段编码本质上就是整份 SVG 文件内容的另一种文本表示 - 两种图标写法的核心差异:Base64 内嵌方式让 WordPress 拿到完整的 SVG 数据,所以能像对待内建 dashicon 一样动态改写
fill颜色(默认灰色,hover 高亮蓝色);而「指向独立文件路径」的写法,浏览器把 SVG 当成普通图片加载,WordPress 没法伸手进去改颜色,图标会保留设计时定的原始颜色(默认半透明,hover 变实色)——两种效果都合法,看设计需求选 add_menu_page()的返回值是这个页面专属的一个 hook 名字符串,存进$mainPageHook变量,用来构造"load-{$mainPageHook}"这个动态钩子名——load-{页面 hook}是 WordPress 提供的「只在某个具体后台页面加载时触发」的钩子模式,比全局挂载再手动判断当前页面更精准、更省性能mainPageAssets()里用标准的wp_enqueue_style()加载 CSS 文件,plugin_dir_url(__FILE__)拿到当前插件目录对应的 URL,拼上文件名即可- 表单目前只是纯 HTML 骨架,
method="POST"但没写action(省略时默认提交到当前页面自己)——处理提交数据的 PHP 逻辑留到下一讲 - CSS 用 Flexbox 让
<textarea>占满除了周围间距外的全部宽度、给固定高度 200px、底部留白 20px,让文本域看起来更像一个正式的大输入框,而不是浏览器默认的小方框
[截图:wp-admin 侧边栏 Word Filter 菜单的自定义 SVG 图标显示效果(灰色默认态/蓝色 hover 态)]
[截图:wp-admin Words List 页面,敏感词逗号分隔文本域与 Save Changes 按钮的样式]
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
add_menu_page() 返回值 | — | 该顶级页面专属的 hook 名,用于构造 load-{hook} 这类只在该页面生效的动态钩子 |
load-{page_hook} | WP 动态 hook | 只在指定的某个具体后台页面加载时触发 |
wp_enqueue_style($handle, $src) | WP 内建 function | 注册并加载一个 CSS 文件 |
plugin_dir_url(__FILE__) | WP 内建 function | 获取当前文件所在插件目录对应的完整 URL |
常见坑
- CSS/JS 资源不区分页面、全局加载——后台每个页面都会被迫加载跟自己无关的样式/脚本,浪费带宽也可能造成样式冲突,应该用
load-{page_hook}这类精确的钩子只在需要的页面加载 - 想要「图标跟随 WordPress 自动配色」却选了「指向独立文件」的写法——浏览器只把它当普通图片处理,WordPress 无法动态改色,达不到预期效果,要用 Base64 内嵌的方式才行
延伸 / 后续讲座会用到
下一讲开始处理表单提交:手写 PHP 接收 $_POST 数据、做安全校验(nonce)、存进数据库。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 23, EP131