前回まででカスタマイズしたSummernoteにWordからコピーした文章をペーストしたら以下のように不要なタグや属性が付与されていた。ペースト時に不要なタグや属性を除去できるsummernote-cleanerプラグインを利用してみる。
動作確認環境
- jQuery(3.7.1)
- Bootstrap(5.3.5)
- Summernote(0.9.1)
- summernote-cleaner(1.0.9)
参考:前回までのソース
summernote-cleanerの取得と導入
jsDelivrを確認したところ、どうやらCDNには少し古いバージョン(1.0.5)しか置いていないようだったので、2025年8月時点の最新バージョンである1.0.9をGitHubのリポジトリから取得し、前回までのカスタマイズソースにsummernote-cleaner.jsファイルを追加した。
root
|-- css
| |-- app.css
| |-- summernote-bs5-editor-custom.css
|-- js
| |-- summernote-cleaner.js
| |-- summernote-init.js
|-- index.html
追加したsummernote-cleanerを使う側で読み込むだけで有効になる。
<!DOCTYPE html>
<html lang="ja">
<head>
<title>Summernoteカスタマイズ</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" type="text/css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.css" type="text/css" />
<link rel="stylesheet" href="./css/app.css" type="text/css" />
<link rel="stylesheet" href="./css/summernote-bs5-editor-custom.css" type="text/css" />
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.bundle.min.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/summernote-bs5.min.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/summernote@0.9.1/dist/lang/summernote-ja-JP.min.js" type="text/javascript"></script>
<script src="./js/summernote-cleaner.js" type="text/javascript"></script>
<script src="./js/summernote-init.js" type="text/javascript"></script>
</head>
<body>
・
・
・この状態で利用するとメモ帳で編集したテキストはペーストできるが、Wordからはできなかった。
開発者ツールで確認してみたところ、Wordからの貼り付け時に以下のエラーが発生していた。

どうやらjQueryで「data-(.*?)」を検索しようとしてエラーとなっているようだった。
summernote-cleanerのデフォルトオプションを確認するとbadAttributesオプションのコメントに「‘data-(.*?)’ would fail when cleaning with jQuery」と記載があったので実装を調べてみた。
$.extend($.summernote.options, {
cleaner: {
action: 'both', // both|button|paste 'button' only cleans via toolbar button, 'paste' only clean when pasting content, both does both options.
icon: '<i class="note-icon"><svg xmlns="http://www.w3.org/2000/svg" id="libre-paintbrush" viewBox="0 0 14 14" width="14" height="14"><path d="m 11.821425,1 q 0.46875,0 0.82031,0.311384 0.35157,0.311384 0.35157,0.780134 0,0.421875 -0.30134,1.01116 -2.22322,4.212054 -3.11384,5.035715 -0.64956,0.609375 -1.45982,0.609375 -0.84375,0 -1.44978,-0.61942 -0.60603,-0.61942 -0.60603,-1.469866 0,-0.857143 0.61608,-1.419643 l 4.27232,-3.877232 Q 11.345985,1 11.821425,1 z m -6.08705,6.924107 q 0.26116,0.508928 0.71317,0.870536 0.45201,0.361607 1.00781,0.508928 l 0.007,0.475447 q 0.0268,1.426339 -0.86719,2.32366 Q 5.700895,13 4.261155,13 q -0.82366,0 -1.45982,-0.311384 -0.63616,-0.311384 -1.0212,-0.853795 -0.38505,-0.54241 -0.57924,-1.225446 -0.1942,-0.683036 -0.1942,-1.473214 0.0469,0.03348 0.27455,0.200893 0.22768,0.16741 0.41518,0.29799 0.1875,0.130581 0.39509,0.24442 0.20759,0.113839 0.30804,0.113839 0.27455,0 0.3683,-0.247767 0.16741,-0.441965 0.38505,-0.753349 0.21763,-0.311383 0.4654,-0.508928 0.24776,-0.197545 0.58928,-0.31808 0.34152,-0.120536 0.68974,-0.170759 0.34821,-0.05022 0.83705,-0.07031 z"/></svg></i>',
keepHtml: true,
keepTagContents: ['span'], //Remove tags and keep the contents
badTags: ['applet', 'col', 'colgroup', 'embed', 'noframes', 'noscript', 'script', 'style', 'title', 'meta', 'link', 'head'], //Remove full tags with contents
badAttributes: ['bgcolor', 'border', 'height', 'cellpadding', 'cellspacing', 'lang', 'start', 'style', 'valign', 'width', 'data-(.*?)'], //Remove attributes from remaining tags, NB. 'data-(.*?)' would fail when cleaning with jQuery
limitChars: 0, // 0|# 0 disables option
limitDisplay: 'both', // none|text|html|both
limitStop: false, // true/false
limitType: 'text', // text|html
notTimeOut: 850, //time before status message is hidden in miliseconds
keepImages: true,
imagePlaceholder: 'https://via.placeholder.com/200'
}
});summernote-cleanerのソースを追いかけていくとペースト時に「cleanHtmlPasteWithjQuery」または「cleanHtmlPasteWithRegExp」関数で不要タグや属性の除去をしているようだった。
var cleanHtmlPaste = function (input, badTags, keepTagContents, badAttributes, keepImages, imagePlaceholder) {
if (typeof (window.jQuery) === 'function') {
return cleanHtmlPasteWithjQuery(input, badTags, keepTagContents, badAttributes, keepImages, imagePlaceholder)
} else {
return cleanHtmlPasteWithRegExp(input, badTags, keepTagContents, badAttributes, keepImages, imagePlaceholder)
}
}画面でjQueryを読み込んでいる場合、「cleanHtmlPasteWithjQuery」の関数で除去処理が実施されるが、badAttributesオプションの「data-(.*?)」が正規表現の形式になっているため、不要な属性の検索でエラーとなってしまうようだ。
解決策としてはsummernote-cleanerの利用側で「data-(.*?)」を削除してしまえば良いため、summrenote-initにsummernote-cleanerの設定を追加した(badAttributes以外のオプションはデフォルトのまま)。
(function($) {
// 日本語未対応の文言を日本語化
$.extend(true, $.summernote.lang, {
'ja-JP': {
font: {
subscript: '下付き',
superscript: '上付き',
},
},
});
// Summernote初期化
$.fn.initSummernote = function (options) {
// デフォルトオプション
const defaultOptions = {
lang: 'ja-JP',
fontSizes: ['8', '9', '10', '11', '12', '14', '16', '18', '20', '22', '24', '26', '28', '36', '48', '72'],
toolbar: [
['style', ['style']],
['font', ['fontsize', 'fontsizeunit', 'forecolor', 'backcolor']],
['fontstyle', ['bold', 'italic', 'strikethrough', 'underline', 'subscript', 'superscript', 'paragraph', 'clear']],
['para', ['ul', 'ol']],
['table', ['table']],
['insert', ['link', 'picture', 'video']],
['operation', ['undo', 'redo']],
['view', ['codeview', 'fullscreen', 'help']],
],
styleTags: [
{tag: 'h1', title: '主見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h1'},
{tag: 'h2', title: '見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h2'},
{tag: 'h3', title: '小見出し', style: 'margin: 4px 0;', className: 'headline', value: 'h3'},
],
cleaner: {
action: 'both',
icon: '<i class="note-icon"><svg height="14" id="libre-paintbrush" viewbox="0 0 14 14" width="14" xmlns="http://www.w3.org/2000/svg"><path d="m 11.821425,1 q 0.46875,0 0.82031,0.311384 0.35157,0.311384 0.35157,0.780134 0,0.421875 -0.30134,1.01116 -2.22322,4.212054 -3.11384,5.035715 -0.64956,0.609375 -1.45982,0.609375 -0.84375,0 -1.44978,-0.61942 -0.60603,-0.61942 -0.60603,-1.469866 0,-0.857143 0.61608,-1.419643 l 4.27232,-3.877232 Q 11.345985,1 11.821425,1 z m -6.08705,6.924107 q 0.26116,0.508928 0.71317,0.870536 0.45201,0.361607 1.00781,0.508928 l 0.007,0.475447 q 0.0268,1.426339 -0.86719,2.32366 Q 5.700895,13 4.261155,13 q -0.82366,0 -1.45982,-0.311384 -0.63616,-0.311384 -1.0212,-0.853795 -0.38505,-0.54241 -0.57924,-1.225446 -0.1942,-0.683036 -0.1942,-1.473214 0.0469,0.03348 0.27455,0.200893 0.22768,0.16741 0.41518,0.29799 0.1875,0.130581 0.39509,0.24442 0.20759,0.113839 0.30804,0.113839 0.27455,0 0.3683,-0.247767 0.16741,-0.441965 0.38505,-0.753349 0.21763,-0.311383 0.4654,-0.508928 0.24776,-0.197545 0.58928,-0.31808 0.34152,-0.120536 0.68974,-0.170759 0.34821,-0.05022 0.83705,-0.07031 z"></path></svg></i>',
keepHtml: true,
keepTagContents: ['span'],
badTags: ['applet', 'col', 'colgroup', 'embed', 'noframes', 'noscript', 'script', 'style', 'title', 'meta', 'link', 'head'],
badAttributes: ['bgcolor', 'border', 'height', 'cellpadding', 'cellspacing', 'lang', 'start', 'style', 'valign', 'width'],
limitChars: 0,
limitDisplay: 'both',
limitStop: false,
limitType: 'text',
notTimeOut: 850,
keepImages: true,
imagePlaceholder: 'https://via.placeholder.com/200'
},
callbacks: {
onInit: function(note) {
// ファイル選択をBootstrapのレイアウトにする
note.editor.find('.note-modal .form-control-file').each(function () {
$(this).addClass('form-control').removeClass('form-control-file');
});
// optionsに拡張プロパティのonInitAfterが設定されてたらコールする
$(this).data('summernote').options?.callbacks?.onInitAfter?.(note);
},
}
}
// オプションをマージ
settings = $.extend(true, {}, defaultOptions, options);
// Summernoteを初期化
$(this).summernote(settings);
return $(this);
};
})(jQuery);これで試してみると無事Wordからコピーした文章を貼付けできており不要なタグや属性が削除されていた。
貼り付け時にdata属性も除去したい場合は、summernote-cleanerの「cleanHtmlPasteWithRegExp」を利用する必要があるが、jQueryが読み込まれていると「cleanHtmlPasteWithjQuery」が呼び出されてしまう。
SummernoteはjQueryが必要なので、必ず「cleanHtmlPasteWithRegExp」を呼び出すようにsummernote-cleanerのソースを修正する必要があると思われる。
summernote-cleanerのカスタマイズ
ペースト時に不要なタグと属性を除去できるようになったが、せっかくなので設定を調整して自分好みにカスタマイズしてみる。
①除去するタグと属性を調整する
badTags、badAttributesプロパティに除去されるタグと属性がそれぞれ設定されているので、これを変更する。
badTagsでは’col’と’colgroup’を除外し、badAttributesでは’width’、’height’、’style’を除外することとした。
・
・
・
cleaner: {
action: 'both',
icon: '<i class="note-icon"><svg xmlns="http://www.w3.org/2000/svg" id="libre-paintbrush" viewBox="0 0 14 14" width="14" height="14"><path d="m 11.821425,1 q 0.46875,0 0.82031,0.311384 0.35157,0.311384 0.35157,0.780134 0,0.421875 -0.30134,1.01116 -2.22322,4.212054 -3.11384,5.035715 -0.64956,0.609375 -1.45982,0.609375 -0.84375,0 -1.44978,-0.61942 -0.60603,-0.61942 -0.60603,-1.469866 0,-0.857143 0.61608,-1.419643 l 4.27232,-3.877232 Q 11.345985,1 11.821425,1 z m -6.08705,6.924107 q 0.26116,0.508928 0.71317,0.870536 0.45201,0.361607 1.00781,0.508928 l 0.007,0.475447 q 0.0268,1.426339 -0.86719,2.32366 Q 5.700895,13 4.261155,13 q -0.82366,0 -1.45982,-0.311384 -0.63616,-0.311384 -1.0212,-0.853795 -0.38505,-0.54241 -0.57924,-1.225446 -0.1942,-0.683036 -0.1942,-1.473214 0.0469,0.03348 0.27455,0.200893 0.22768,0.16741 0.41518,0.29799 0.1875,0.130581 0.39509,0.24442 0.20759,0.113839 0.30804,0.113839 0.27455,0 0.3683,-0.247767 0.16741,-0.441965 0.38505,-0.753349 0.21763,-0.311383 0.4654,-0.508928 0.24776,-0.197545 0.58928,-0.31808 0.34152,-0.120536 0.68974,-0.170759 0.34821,-0.05022 0.83705,-0.07031 z"/></svg></i>',
keepHtml: true,
keepTagContents: ['span'],
badTags: ['applet', 'embed', 'noframes', 'noscript', 'script', 'style', 'title', 'meta', 'link', 'head'],
badAttributes: ['bgcolor', 'border', 'cellpadding', 'cellspacing', 'lang', 'start', 'valign'],
limitChars: 0,
limitDisplay: 'both',
limitStop: false,
limitType: 'text',
notTimeOut: 850,
keepImages: true,
imagePlaceholder: 'https://via.placeholder.com/200'
},
・
・
・②入力可能文字数を制限する
limitCharsなど、limit〇〇のプロパティで入力可能文字数を制限できる。今回は以下の設定に変更した。
| プロパティ | 設定値 | 備考 |
| limitType | ‘text’ | テキストの文字数で制限をかける(HTMLではない) |
| limitChars | 3000 | 入力可能文字数を3000文字に制限 |
| limitStop | true | 入力可能文字数に達したら入力を制限する |
| limitDisplay | ‘text’ | ステータス領域の表示内容をテキストの文字数のみにする |
・
・
・
cleaner: {
action: 'both',
icon: '<i class="note-icon"><svg xmlns="http://www.w3.org/2000/svg" id="libre-paintbrush" viewBox="0 0 14 14" width="14" height="14"><path d="m 11.821425,1 q 0.46875,0 0.82031,0.311384 0.35157,0.311384 0.35157,0.780134 0,0.421875 -0.30134,1.01116 -2.22322,4.212054 -3.11384,5.035715 -0.64956,0.609375 -1.45982,0.609375 -0.84375,0 -1.44978,-0.61942 -0.60603,-0.61942 -0.60603,-1.469866 0,-0.857143 0.61608,-1.419643 l 4.27232,-3.877232 Q 11.345985,1 11.821425,1 z m -6.08705,6.924107 q 0.26116,0.508928 0.71317,0.870536 0.45201,0.361607 1.00781,0.508928 l 0.007,0.475447 q 0.0268,1.426339 -0.86719,2.32366 Q 5.700895,13 4.261155,13 q -0.82366,0 -1.45982,-0.311384 -0.63616,-0.311384 -1.0212,-0.853795 -0.38505,-0.54241 -0.57924,-1.225446 -0.1942,-0.683036 -0.1942,-1.473214 0.0469,0.03348 0.27455,0.200893 0.22768,0.16741 0.41518,0.29799 0.1875,0.130581 0.39509,0.24442 0.20759,0.113839 0.30804,0.113839 0.27455,0 0.3683,-0.247767 0.16741,-0.441965 0.38505,-0.753349 0.21763,-0.311383 0.4654,-0.508928 0.24776,-0.197545 0.58928,-0.31808 0.34152,-0.120536 0.68974,-0.170759 0.34821,-0.05022 0.83705,-0.07031 z"/></svg></i>',
keepHtml: true,
keepTagContents: ['span'],
badTags: ['applet', 'embed', 'noframes', 'noscript', 'script', 'style', 'title', 'meta', 'link', 'head'],
badAttributes: ['bgcolor', 'border', 'cellpadding', 'cellspacing', 'lang', 'start', 'valign'],
limitChars: 3000,
limitDisplay: 'text',
limitStop: true,
limitType: 'text',
notTimeOut: 850,
keepImages: true,
imagePlaceholder: 'https://via.placeholder.com/200'
},
・
・
・③クリーナーボタンをツールバーに表示する
クリーナーボタンを表示すると、ボタン押下で本文全体に対して不要なタグと属性を除去を実行できるようになる。
actionプロパティが’both’または’button’であれば以下の設定を追加することで表示できる。
※本文全体に対して適用されるので文章が長いとかなり時間がかかるため注意が必要
・
・
・
// デフォルトオプション
const defaultOptions = {
lang: 'ja-JP',
fontSizes: ['8', '9', '10', '11', '12', '14', '16', '18', '20', '22', '24', '26', '28', '36', '48', '72'],
toolbar: [
['style', ['style']],
['font', ['fontsize', 'fontsizeunit', 'forecolor', 'backcolor']],
['fontstyle', ['bold', 'italic', 'strikethrough', 'underline', 'subscript', 'superscript', 'paragraph', 'clear']],
['para', ['ul', 'ol']],
['table', ['table']],
['insert', ['link', 'picture', 'video']],
['cleaner', ['cleaner']],
['operation', ['undo', 'redo']],
['view', ['codeview', 'fullscreen', 'help']],
],
・
・
・④クリーナーボタンのツールチップやメッセージの文言をカスタマイズする
クリーナーボタンにマウスをフォーカスした際のツールチップや、除去実行時にステータスバーに表示される文言を自分好みに変更した。
(function($) {
// 日本語未対応の文言を日本語化
$.extend(true, $.summernote.lang, {
'ja-JP': {
font: {
subscript: '下付き',
superscript: '上付き',
},
cleaner: {
tooltip: '不要なタグを除去',
not: '不要なタグを除去しました',
limitText: '入力可能文字数',
limitHTML: '入力可能文字数(タグ含む)'
},
},
});
・
・
・


コメント