「記事デザイン」はHTML編集を開くと失調する

最近の編集環境で、HTML編集を開くとおかしな事になるのが目につきます。 3月に差し戻された更新編集画面は、HTML編集を一旦開いて通常表示に戻ると、全く無関係のリンクカードが分解する致命的なバグがありました。

 

それより少し前の2月中旬にリリースされた「記事デザイン」も、HTML編集に1度入ると「失調」する部分があります。 問題は軽微ですが、インターフェイスとしてはいまいちです。

 

 

 

 テキスト部分の「自動選択」が無効になる

最初に編集パレットでデザインを選択してクリックすると、編集枠にデザイン枠が生成されます。 この時、「テキストを入力」というサンプルの文字列が反転選択された状態になっています。

 

 

反転表示で入力部が判り易くなり、この状態でキーボードから入力すると、「テキストを入力」の文字が消えます。 この反転選択の状態は「テキストを入力」の文字を消し易くする目的があるのかも知れません。 また、飾り枠内にコピーして来た「文字列」「画像」「動画」などを貼り付ける操作が簡単になると言えます。

 

編集枠内の他の場所のクリックで反転状態は解除されますが、「テキストを入力」をクリックすると、再び反転選択になります。 この部分は「自動選択」が設定されてるわけですが、それが一度でも「HTML編集」を開くと無効になります。

 

下は、「HTML編集」を開いていない場合です。

 

 

下は、一度「HTML編集」を開いた場合です。

 

 

一度「HTML編集」を開くと、「テキストを入力」をクリックしても選択されなくなります。 新しく飾り枠を生成すると最初は反転しているので、殆ど問題にならないと考えて、そのままにしたのかも知れませんが。

 

 

 

対策コード 

「テキストを入力」の部分を反転選択するには、「selection」「range」等を使う必要があります。 これは何度やってもややこしいですね。

 

問題の部分は、「記事デザイン」に共通で「data-entrydesign-content」の属性が指定されてます。 これをクリックすれば反転選択になるスクリプトを作りました。

 

let d_content=iframe_doc.querySelectorAll('[data-entrydesign-content]');
for(let k=0; k<d_content.length; k++){
    d_content[k].addEventListener('click', function(){
        get(d_content[k]); }); }

function get(elem){
    let selection=iframe_doc.getSelection();
    let range=iframe_doc.createRange();
    range.selectNodeContents(elem);
    selection.removeAllRanges();
    selection.addRange(range); }

 

後半の関数「get()」は、引数「elem」を選択状態にし、マウスを左クリックしながらドラッグして反転選択するのと、全く同結果になります。

 

上記のコードだけでは、未だ少し問題があります。「Rep Heading ⭐」のメイン機能の「h要素」変換を行うと、直後は要素が書換えられて選択状態が解除されます。 これは、変換後にすぐに再取得して、反転選択する必要があります。

 

この再取得は前ページの「ver. 0.2」で既に対応していますが、要素を「click」して反転選択にする少々不完全なコードで、今回は「get()」に改めました。

 

下は、「見出しの記事デザイン」の処理直後に実行する関数です。 2行目で、デザイン処理対象の全てを再取得し、変更した対象のインデックス「n」は変わらないので、それを使って取得した変更後の対象に「get()」を実行しています。 

 

function rep_tag_mend(n){
    let h_elem=iframe_doc.querySelectorAll('[data-entrydesign-type="heading"]');
    if(h_elem[n]){
        let content=h_elem[n].querySelector('[data-entrydesign-content]');
        if(content){
            get(content); }}}

 

一方「テキストの記事デザイン」は、処理が異なります。 こちらは、書換え処理を飾り枠の一番低い階層で行うので、変換した要素の再取得が簡単です。(2行目)

その後半のコードは、「h要素」の既定の大きな文字サイズ等のリセット処理で、最後に「get()」で、変換後の要素を反転選択しています。

 

function rep_ptag_mend(element){
    let p_elem=element.querySelector('[data-entrydesign-content] :last-child');
    if(p_elem){
        p_elem.style.margin='0';
        p_elem.style.lineHeight='inherit';
        p_elem.style.fontSize='inherit';
        get(p_elem); }}

 

以上の 3個のコードによって、反転選択の状態は良好に維持されます。

 

 

「記事デザイン」の扱いについて 

「テキストを入力」の部分の反転選択について細かな補完をしたのですが、ここに文字を書く際に、「Delete」「Backspace」は用心して使わないと「記事デザイン」の飾り枠全体が削除されてしまいます。 枠が生成されたら、反転選択は文字入力の1文字目で消える事に慣れた方が良さそうです。

 

 

 

「Rep Heading ⭐」を利用するには

このツールは Chrome / Edge / Firefox版の拡張機能「Tampermonkey」上で動作します。 以下に、このツールの導入手順を簡単に説明します。

 

❶「Tampermonkey」を導入します

◎ 使用しているブラウザに拡張機能「Tampermonkey」を導入する事が必要です。

既に「Tampermonkey」を導入している場合は、この手順 ❶ は不要です。 

拡張機能の導入については、以下のページに簡単な説明があるので参照ください。

 

 

❷「Tampermonkey」にスクリプトを登録します

◎「Tampermonkey」の「」マークの「新規スクリプト」タブを開きます。

 

 

 

◎「新規スクリプト」には、最初からテンプレートが記入されています。 これは全て削除して、完全に空白の編集枠に 下のコードをコピー&ペーストします。

 

〔コピー方法〕 軽量シンプルなツール「PreBox Button   」を使うと

  コード枠内を「Ctrl+左Click」➔「Copy code 」を「左Click」

  の操作で、掲載コードのコピーが可能になります。

 

◎ 最後に「ファイル」メニューの「保存」を押すと、ツールが使用可能になります。

 

 

〔 Rep Heading ⭐ 〕 ver. 0.3

 

// ==UserScript==
// @name         Rep Heading ⭐
// @namespace  http://tampermonkey.net/
// @version      0.3
// @description   記事デザインの「h2」「h3」「テキスト」を「Ctrl+左Click」で変換する
// @author       Ameba Blog User
// @match        https://blog.ameba.jp/ucs/entry/srventry*
// @exclude      https://blog.ameba.jp/ucs/entry/srventrylist.do*
// @grant        none
// ==/UserScript==


let retry=0;
let interval=setInterval(wait_target, 100);
function wait_target(){
    retry++;
    if(retry>10){ // リトライ制限 10回 1sec
        clearInterval(interval); }
    let target=document.getElementById('cke_1_contents'); // 監視 target
    if(target){
        main(); }}



function main(){
    let target=document.getElementById('cke_1_contents'); // 監視 target
    let monitor=new MutationObserver(tag_manager);
    monitor.observe(target, {childList: true});

    tag_manager();

} // main()



function tag_manager(){
    let style=
        '<style class="rep_head">h2::before { content: "h2"; position: absolute; left: -36px; '+
        'padding: 2px 4px 0; height: 18px; font: bold 16px/19px Meiryo; '+
        'border: 1px solid #aaa; background: #fff; color: red; } '+
        'h3::before { content: "h3"; position: absolute; left: -36px; '+
        'padding: 2px 4px 0; height: 18px; font: bold 16px/19px Meiryo; '+
        'border: 1px solid #aaa; background: #fff; color: #000; } '+
        'h4::before { content: "h4"; position: absolute; left: -36px; '+
        'padding: 2px 4px 0; height: 18px; font: bold 16px/19px Meiryo; '+
        'border: 1px solid #aaa; background: #fff; color: #888; }</style>';

    let editor_iframe=document.querySelector('.cke_wysiwyg_frame');
    if(editor_iframe){
        let iframe_doc=editor_iframe.contentWindow.document;
        if(iframe_doc){
            if(!iframe_doc.querySelector('.rep_head')){
                iframe_doc.documentElement.insertAdjacentHTML('beforeend', style); }}}


    if(editor_iframe){
        let iframe_doc=editor_iframe.contentWindow.document;
        if(iframe_doc){
            let target=iframe_doc.body; // 監視 target
            if(target){
                let monitor=new MutationObserver(replace_tag);
                monitor.observe(target, {childList: true}); }}}

    replace_tag();

} // tag_manager()



function replace_tag(){
    let editor_iframe=document.querySelector('.cke_wysiwyg_frame');
    if(editor_iframe){
        let iframe_doc=editor_iframe.contentWindow.document;
        if(iframe_doc){

            let h_elem=iframe_doc.querySelectorAll('[data-entrydesign-type="heading"]');
            for(let k=0; k<h_elem.length; k++){
                h_elem[k].onclick=function(event){
                    if(event.ctrlKey){
                        event.stopImmediatePropagation();
                        rep_tag(h_elem[k]);
                        rep_tag_mend(k); }}}


            function rep_tag(element){
                let alt_elem=element.outerHTML.toString();

                if(witch_h(element)==2){
                    alt_elem=alt_elem.replace(/^<h2/, '<h3').replace(/h2>$/, 'h3>'); }
                else if(witch_h(element)==3){
                    alt_elem=alt_elem.replace(/^<h3/, '<div').replace(/h3>$/, 'div>'); }
                else if(witch_h(element)==0){
                    alt_elem=alt_elem.replace(/^<div/, '<h2').replace(/div>$/, 'h2>'); }

                element.outerHTML=alt_elem; } // rep_tag() h2➔h3➔div の書換


            function rep_tag_mend(n){
                let h_elem=iframe_doc.querySelectorAll('[data-entrydesign-type="heading"]');
                if(h_elem[n]){
                    let content=h_elem[n].querySelector('[data-entrydesign-content]');
                    if(content){
                        get(content); }}}



            let hd_elem=iframe_doc.querySelectorAll('[data-entrydesign-type="block"]');
            for(let k=0; k<hd_elem.length; k++){
                hd_elem[k].onclick=function(event){
                    if(event.ctrlKey){
                        event.stopImmediatePropagation();
                        rep_ptag(hd_elem[k]);
                        rep_ptag_mend(hd_elem[k]); }}}


            function rep_ptag(element){
                let p_elem=element.querySelector('[data-entrydesign-content] :last-child');
                if(p_elem){
                    let alt_elem=p_elem.outerHTML.toString();

                    if(witch_h(p_elem)==2){
                        alt_elem=alt_elem.replace(/^<h2/, '<h3').replace(/h2>$/, 'h3>'); }
                    else if(witch_h(p_elem)==3){
                        alt_elem=alt_elem.replace(/^<h3/, '<p').replace(/h3>$/, 'p>'); }
                    else if(witch_h(p_elem)==0){
                        alt_elem=alt_elem.replace(/^<p/, '<h2').replace(/p>$/, 'h2>'); }

                    p_elem.outerHTML=alt_elem; }} // rep_ptag() h2➔h3➔p の書換


            function rep_ptag_mend(element){
                let p_elem=element.querySelector('[data-entrydesign-content] :last-child');
                if(p_elem){
                    p_elem.style.margin='0';
                    p_elem.style.lineHeight='inherit';
                    p_elem.style.fontSize='inherit';
                    get(p_elem); }}



            function witch_h(elem){
                if(elem.tagName=='H2'){
                    return 2; }
                else if(elem.tagName=='H3'){
                    return 3; }
                else return 0; }



            let d_content=iframe_doc.querySelectorAll('[data-entrydesign-content]');
            for(let k=0; k<d_content.length; k++){
                d_content[k].addEventListener('click', function(){
                    get(d_content[k]); }); }


            function get(elem){
                let selection=iframe_doc.getSelection();
                let range=iframe_doc.createRange();
                range.selectNodeContents(elem);
                selection.removeAllRanges();
                selection.addRange(range); }

        }}} // replace_tag()


 

 

 

「Rep Heading ⭐」最新版について 

旧いバージョンの JavaScriptツールは、アメーバのページ構成の変更で動作しない場合があり、導入する場合は最新バージョンをお勧めします。

 

●「Rep Heading ⭐」の最新バージョンへのリンクは、以下のページのリンクリストから探せます。