結論 LESSを使う
単なるCSSファイルを読み込む場合もLESSファイルとして読み込む必要があります。
でなければ、スコープ内に限定したCSS適用が出来ません
.note-editable {
@import (less) "/css/reset.css";
@import (less) "/css/base.css";
@import (less) "/css/styles.css";
}
<link rel="stylesheet" type="text/css" media="all" href="/admin/plugins/summernote/summernote-bs4.css">
<script type="text/javascript" charset="utf-8" src="/admin/plugins/jquery/jquery.min.js"></script>
<script type="text/javascript" charset="utf-8" src="/admin/plugins/bootstrap-4.1.3/js/bootstrap.bundle.min.js"></script>
<script type="text/javascript" charset="utf-8" src="/admin/plugins/summernote/summernote-bs4.js"></script>
<!-- LESSファイルを読み込む際は rel="stylesheet/less" と書くことに注意 -->
<link rel="stylesheet/less" type="text/css" media="all" href="/admin/css/import.less" />
<script src="//cdnjs.cloudflare.com/ajax/libs/less.js/3.9.0/less.min.js" ></script>
<scirpt>
$(function(){
$('#sample').summernote();
});
</scirpt>
<textarea name="sample" id="sample"></textarea>
背景
- 管理画面にSummernoteを使ってWYSIWYGエディタを実装した
- WYSIWYGエディタで入力したHTMLデータはDBに格納され、PHPなどを経由してユーザ向け画面にHTMLとして描写される
- ユーザー向け画面は、デザイナーとか、自分以外の人間が書いたCSSが適用される
- 管理画面のWYSIWYGエディタでも、出来る限りユーザ向け画面と同じ見た目でHTMLが記述できるようにしたい
やりたいこと
- Summernoteのエディタ「内」に、ユーザ向け画面のCSS(外部CSS)を適用したい
何が問題か
- ユーザ向け画面のCSSは今後も変更される可能性があり、自分では編集できない
- SummernoteのHTML表現は、元のHTML内部にdivなどの要素を追加する形である
- そのまま外部CSSを適用すれば、WYSIWYGエディタの外側である管理画面にもCSSの影響が出て、レイアウト崩れが発生
考え方
- SummernoteのWYSIWYGエディタ「内」とは、div.note-editable の中のことである
- このdiv.note-editableの中にだけ、次のような形でCSSが適用できればそれで要件は達成できる
.note-editable h1 {
font-size: 24px;
}
.note-editable h2 {
font-size: 20px;
}
.note-editable h3 {
font-size: 18px;
}
.note-editable .hoge ...
ユーザ向け画面のCSSは今後も事前の予告なく書き換わることが十分に考えられるため、
別名でコピーして全部のセレクタ―に.note-editable
を付け加えまくるのは
メンテナンス性が悪すぎますし、そもそもやりたくありません。
ユーザ向け画面のCSSを、特定のセレクタの下にimportすることができれば、
ユーザ向け画面のCSSがいくら書き換わろうが、管理画面側(私側)は一切手を動かさないで済みます。
この願いをかなえてくれるのが、LESSでした。
余談
TinyMCEならば、content_cssというプロパティを用いて、エディタ内に外部CSSを適用できます。
TinyMCEはiframeで動いているため、iframe内にCSSを適用してもその外側(管理画面自体)にはレイアウト崩れなどの影響がありません。
もしこれを読んでいるあなたがまだ、WYSIWYGエディタを実装していないのであれば、TinyMCEを利用することをオススメします。。
参考にしたURL
- https://github.com/summernote/summernote/issues/329
- https://documentation.concrete5.org/tutorials/easily-scopenamespace-theme-and-block-css-using-less-and-sass-to-prevent-css-conflicts
- https://stackoverflow.com/questions/42425243/editor-html-using-rows-and-columns-from-bootstrap
追記
デザイナーから提供された外部CSSが次のような形で、
特定のクラスの配下にあることを前提としたものだった場合。
.entry h1 {
font-size : 24px;
}
.entry h2 {
font-size : 20px;
}
順当に考えれば、次のようにSummernoteエディタ上で
そのクラスで囲んだHTMLを用意すれば、期待したレイアウトで表示されるはずです
<div class="entry">
<h1>h1テキスト</h1>
<h2>h2テキスト</h2>
</div>
ですが、オペレータにおまじないで毎回divを書いてくれと言うのはヒューマンエラーのもと。
それに、DBに親要素の情報まで格納するのは今後、サイトリニューアルがあった時に足枷になりかねない。
ので、おまじないナシで、オペレータには次のような入力をさせて、
ユーザ向け画面のCSSを表示したいと思います。
<h1>h1テキスト</h1>
<h2>h2テキスト</h2>
考え方
SummernoteのHTML構成は次のような形になっています
<div class="note-editing-area">
<div class="note-placeholder" style="display: none;">
placeholder text is here
</div>
<div class="note-handle">
<div class="note-control-selection" style="display: none;">
<div class="note-control-selection-bg"></div>
<div class="note-control-holder note-control-nw"></div>
<div class="note-control-holder note-control-ne"></div>
<div class="note-control-holder note-control-sw"></div>
<div class="note-control-sizing note-control-se"></div>
<div class="note-control-selection-info"></div>
</div>
</div>
<textarea aria-multiline="true" class="note-codable" role="textbox"></textarea>
<div aria-multiline="true" class="note-editable card-block" contenteditable="true" role="textbox">
<h1>h1テキスト</h1>
<h2>h2テキスト</h2>
</div>
</div>
LESSによって今、次のような形で外部CSSが表現されています
.note-editable .entry h1 {
font-size : 24px;
}
.note-editable .entry h2 {
font-size : 20px;
}
.note-editable
に外部CSSの親要素のクラス名を付与し、
次のようなCSS組みが出来れば、要件は達成できそうです
.note-editing-area .entry h1 {
font-size : 24px;
}
.note-editing-area .entry h2 {
font-size : 20px;
}
実装
LESSの記述を変更
.note-editing-area {
@import (less) "/css/reset.css";
@import (less) "/css/common.css";
@import (less) "/css/category.css";
}
エディタに親要素のクラスを付与
$(function(){
$(".note-editable").addClass("entry");
});
さらに追記
一応Summenoteの公式にもこの件でチケット起票していたのですが、
公式には対応する手段はないとのことでクローズされました(絶望)