URL表記に a要素が自動生成される問題

アメブロの通常表示編集枠に「https://blog.ameba.jp」などの「URL」の文字列をペーストすると、Chromeでは自動的に「a要素」が生成されます。 Firefoxは動作が異なり、コピー元が単なる文字列の場合は「a要素」は生成されません。

 

下は、「Tampermonkey」のコード編集画面から、アメブロの編集枠にコードをペーストしたサンプルです。

 

 

「UserScript」のパラメータ部の青い文字列は、「Tampermonkey」側には無かった「a要素」が自動的に生成され、リンクになった事を示しています。

 

「HTML表示」に切り替えると、下の様に盛大に「&nbsp」が生成されています。

 

 

下は「To Space(nbsp)」で「&nbsp」を半角空白に置換えた所です。

 

 

コード内容が判り易くなりましたが、青枠はペースト時に生成された「a要素」です。

「pre枠」の目的に支障はありませんが、この「a要素」は不要でいただけません。 実は「Tampermonkey」のスクリプト記事を書くたび、このリンクを削除しています。

 

 

アメブロの編集枠は、リンク表示をクリックして「a要素」を削除出来ます。 これは気の利いた大変良い機能です。 しかし、スクリプトコードのずっと下方で「a要素」が勝手に生成されると、それを見落としかねません。 そこで、この類の「a要素」を自動削除する機能も実装しました。

 

 

 

a要素の自動削除

a_count=0;
buffer[i]=pre_box[i].firstChild.innerHTML; // 選択pre枠のデータバックアップ
let child_node=pre_box[i].firstChild.childNodes;

for(let t=0; t<child_node.length; t++){
    if(child_node[t].nodeType==3){
        child_node[t].nodeValue=child_node[t].nodeValue.replace(/\u00A0/g , '\u0020'); }
    else if(child_node[t].nodeType==1 && child_node[t].tagName=="A"){
        let inner_node=child_node[t].childNodes;
        if(inner_node.length==1 && inner_node[0].nodeType==3){
            pre_box[i].firstChild.replaceChild(inner_node[0], child_node[t]); } // text を a要素と入替え
        else{
            a_count +=1; }} // 子ノードが1個の textの条件に当てはまらない a要素
    else{ // ここからは同じ

 

整形エンジンの主要部ですが、これまでのコードに青文字部分を割込ませています。

 

「pre枠」の子ノードで「nodeType==3」のテキストノードは変換処理。 それ以外の子ノードで、「nodeType==1」の要素ノードで且つ、タグ名が「A」の場合に、問題とする「a要素」と、第一段階の判断をします。

 

更に、「a要素」の子ノードが1個で textノードであれば、自動生成された「a要素」と断定して、「a要素」をその内部の「textノード」で置き換えます。 それ以外の複雑な構成の「a要素」の場合は、自動生成されたものではなく、何らかのコード内容と考えて、処理をせずに「a_count」でその例外の個数を数え、後で報告します。

 

想定外の「a要素」がコード内にあるかも知れないので、安易にはテキスト置換の処理をしない様にしています。

 

 

a要素の自動削除の実際の動作

下は、「Tampermonkey」のユーザースクリプトの @パラメーター部で、標準的な例で、自動生成された「a要素」が2個あります。

 

 

新しい整形コードの処理は、2個の「a要素」を普通のテキストに置変えます。

 

 

意図的に「a要素」にリンクアイコンを付加したものをテストすると、その「a要素」だけは下の様に処理されずに残ります。

 

 

この様な問題があった場合は、「&nbsp」と「a要素」の処理を同時に行った後で、それぞれ例外で処理されずに残った数を報告する様にしました。

 

 

例外がなく処理が完了した場合は、青枠を表示するだけにしています。 

 

通常表示の編集画面から移動しない限り、処理した枠を元の状態に戻せます。 この復帰方法は少し改めて、処理例外の有無にかかわらず、常に元に戻す様にしています。

 

 

 

BR Checker / To Space (nbsp) のコード

 

〔 BR Checker / To Space (nbsp) 〕ver. 0.7

// ==UserScript==
// @name         BR Checker / To Space (nbsp)
// @namespace    http://tampermonkey.net/
// @version      0.7
// @description  Blogの書式整形ツール
// @author        Ameba blog User
// @match        https://blog.ameba.jp/ucs/entry/srventry*
// @grant         none
// ==/UserScript==

window.addEventListener('load', function(){
    'use strict';

    var marked1=0; // BR Checker 起動・非起動の指標
    var marked2=0; // To Space (nbsp) 起動・非起動の指標
    var target;
    var editor_iframe;
    var iframe_doc;
    var iframe_body;
    var pre_box; // PRE枠のコンテナブロック

    target=document.getElementById('cke_1_contents'); // 監視 target
    let monitor=new MutationObserver(catch_key);
    monitor.observe(target, {childList: true}); // ショートカット待受け開始

    catch_key();


function catch_key(){
    let send;

    editor_iframe=document.querySelector('.cke_wysiwyg_frame');
    if(editor_iframe){ // WYSIWYG表示が実行条件

        if(marked1==1){
            sign(); }
        if(marked2==1){
            active(); }

        iframe_doc=editor_iframe.contentWindow.document;
        iframe_doc.addEventListener("keydown", check_key);
        document.addEventListener("keydown", check_key);

        function check_key(event){
            let gate=-1;
            if(event.which==17 || event.ctrlKey==true){
                if(event.which==120 || event.keyCode==120){
                    event.preventDefault(); send=120; gate=1; }} // ショートカット「Ctrl+F9」
            if(event.which==17 || event.ctrlKey==true){
                if(event.which==121 || event.keyCode==121){
                    event.preventDefault(); send=121; gate=1; }} // ショートカット「Ctrl+F10」

            if(editor_iframe){
                if(gate==1){
                    event.stopImmediatePropagation();
                    event.preventDefault();
                    set_task(send); }}} // check_key
}} // catch_key


function set_task(send){
    if(send==120){ //「Ctrl+F9」 BR Checker スイッチ
        if(marked1==0 && marked2==0){
            marked1=1;
            sign(); } // BR Checker をON
        else if(marked1==1){
            marked1=0;
            sign_clear(); }} // BR Checker をOFF
    if(send==121){ //「Ctrl+F10」 To Space (nbsp) スイッチ
        if(marked1==0 && marked2==0){
            marked2=1;
            active(); } // To Space (nbsp) をON
        else if(marked2==1){
            marked2=0;
            disable(); }} // To Space (nbsp) をOFF
} // set_task


// ********** BR Checker  Functions **************

function sign(){
    let br_tag;
    let br_mark;
    let i_tag;
    let c_br=0;
    let css='';
    let disp;

    editor_iframe=document.querySelector('.cke_wysiwyg_frame');
    editor_iframe.onload=function() {
        iframe_doc=editor_iframe.contentWindow.document; }

    i_tag=iframe_doc.querySelectorAll('.brmark');
    if(i_tag.length >=1){
        for(let i=0; i < i_tag.length; i++){ i_tag[i].remove(); }} // BRマーク削除

    br_tag=iframe_doc.querySelectorAll('br');
    for(let i=0; i < br_tag.length; i++){
        if(br_tag[i].parentNode.tagName=="P" && br_tag[i].parentNode.childNodes.length !=1){
            c_br +=1;
            br_mark=iframe_doc.createElement("i");
            br_mark.appendChild(iframe_doc.createTextNode("▼"));
            br_mark.setAttribute("class", "brmark");
            br_mark.setAttribute("style", "display: inline-block;width: 0;margin-left: -2px;color: red");
            br_tag[i].parentNode.insertBefore(br_mark, br_tag[i]); }
        else{ continue; }} // BRマーク表示

    css +=['display: inline-block; margin: 0 0 4px 8px; padding: 4px 15px 1px;',
           'font-size: 16px; color: #fff; background: red'].join(' ');
    disp=document.createElement("span");
    disp.appendChild(document.createTextNode("▼ BR Checker"));
    disp.setAttribute("style", css);
    disp.appendChild(document.createTextNode(" count : " + c_br));

    monitor.disconnect();

    if(target.firstChild.tagName!="IFRAME"){
        target.firstChild.remove();
        target.insertBefore(disp, editor_iframe); }
    else{
        target.insertBefore(disp, editor_iframe); }

    monitor.observe(target, {childList: true}); } // BR Checker 起動表示


function sign_clear(){
    let i_tag;

    editor_iframe=document.querySelector('.cke_wysiwyg_frame');
    iframe_doc=editor_iframe.contentWindow.document;

    if(target.firstChild.tagName !="IFRAME"){
        target.firstChild.remove(); } // BR Checker 起動表示を削除

    i_tag=iframe_doc.querySelectorAll('.brmark');
    if(i_tag.length >=1){
        for(let i=0; i < i_tag.length; i++){ i_tag[i].remove(); }}} // BRマーク削除


// ********** To Space (nbsp)  Functions **************


function active(){
    let buffer;
    let nbsp;
    let a_count;
    let css='';
    let disp;

    monitor.disconnect();

    css +=['display: inline-block; margin: 0 0 4px 8px; padding: 4px 50px 1px;',
           'font-size: 16px; color: #fff; background: #03a9f4'].join(' ');
    disp=document.createElement("span");
    disp.appendChild(document.createTextNode("To Space (nbsp)"));
    disp.setAttribute("style", css);
    if(target.firstChild.tagName=="IFRAME"){
        target.insertBefore(disp, editor_iframe); } // To Space (nbsp) 起動表示

    monitor.observe(target, {childList: true});

    iframe_doc=editor_iframe.contentWindow.document;
    iframe_body=iframe_doc.querySelector('body.cke_editable');
    pre_box=iframe_body.querySelectorAll('div');
    buffer=Array(pre_box.length);

    for(let i=0; i<pre_box.length; i++){
        select_pre_box(i); }

    function select_pre_box(i){
        pre_box[i].oncontextmenu=function(){
            if(pre_box[i].firstChild.tagName=='PRE'){
                select_box(select_do);
                return false;

                function select_box(select_do){
                    boxshadow();
                    setTimeout( select_do, 100); }
                function boxshadow(){
                    pre_box[i].style.outline='2px solid #03a9f4'; }
                function select_do(){ start_select(i); }}}}

    function start_select(i){
        let regex=new RegExp('\u00A0', 'g');
        nbsp=pre_box[i].innerText.match(regex);
        let pa_count=pre_box[i].getElementsByTagName("a");

        if(buffer[i] !=null){
            let ok=confirm(" ❎  変換前に戻しますか?");
            if(ok){
                pre_box[i].firstChild.innerHTML=buffer[i];
                buffer[i]=null;
                pre_box[i].style.outline='none'; }}
        else{
            if(nbsp !=null || pa_count !=null){
                let ok=confirm([" ⏬  「&nbsp」 " + nbsp.length + " 個を半角空白に変換します\n",
                                       "    「a要素」 " + pa_count.length + " 個をテキストに置換えます"].join(''));
                if(ok){ to_space(i); }
                else{ pre_box[i].style.outline='none'; }}
            else{ ; }}}


    function to_space(i){
        a_count=0;
        buffer[i]=pre_box[i].firstChild.innerHTML; // 選択pre枠のデータバックアップ
        let child_node=pre_box[i].firstChild.childNodes;

        for(let t=0; t<child_node.length; t++){
            if(child_node[t].nodeType==3){
                child_node[t].nodeValue=child_node[t].nodeValue.replace(/\u00A0/g , '\u0020'); }
            else if(child_node[t].nodeType==1 && child_node[t].tagName=="A"){
                let inner_node=child_node[t].childNodes;
                if(inner_node.length==1 && inner_node[0].nodeType==3){
                    pre_box[i].firstChild.replaceChild(inner_node[0], child_node[t]); } // text を a要素と入替え
                else{
                    a_count +=1; }} // 子ノードが1個の textの条件に当てはまらない a要素
            else{
                let child_node2=child_node[t].childNodes;
                if(child_node2){
                    for(let t2=0; t2<child_node2.length; t2++){
                        if(child_node2[t2].nodeType==3){
                            child_node2[t2].nodeValue=child_node2[t2].nodeValue.replace(/\u00A0/g , '\u0020'); }
                        else{
                            let child_node3=child_node2[t2].childNodes;
                            if(child_node3){
                                for(let t3=0; t3<child_node3.length; t3++){
                                    if(child_node3[t3].nodeType==3){
                                        child_node3[t3].nodeValue=child_node3[t3].nodeValue.replace(/\u00A0/g , '\u0020'); }}}}}}}}

        let regex=new RegExp('\u00A0', 'g');
        nbsp=pre_box[i].innerText.match(regex);
        if(nbsp !=null){
            alert("❌ 変換できなかった「&nbsp」の数 : " + nbsp.length );}
        if(a_count !=0){
            alert("❌ 置換えが出来ない「a要素」 : " + a_count); }}}


function disable(){
    if(target.firstChild.tagName !="IFRAME"){
        target.firstChild.remove(); } // To Space (nbsp) 起動表示を削除

    for(let i=0; i<pre_box.length; i++){
        if(pre_box[i].firstChild.tagName=='PRE'){
            pre_box[i].style.outline='none'; }} // 処理済枠の outline を削除
    pre_box=[]; }// 動作の無効化

});

 

 

 

「BR Checker」「To Space (nbsp)」最新版について 

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

 

●「BR Checker」は、後に「BR Checker+Outro Style ⭐」に統合されています。 その最新バージョンへのリンクは、以下のページのリンクリストから探せます。

 

 

●「To Space (nbsp)」は、後に「PreBox Tools ⭐」に統合されています。 その最新バージョンへのリンクは、以下のページのリンクリストから探せます。