FC2ブログNEWマークのdocument.write書き換えコード

FC2ブログNEWマークのdocument.write書き換えコード

カスタマイズ HTML, CSS, JavaScript
2019/11/09
0
vanillaice (Akira)
vanillaice (Akira)
JavaScriptVanillaJSTipsInstruction

「ひとつの方法」です。やり方はそれこそ無数にありますので、現段階でこの方法が良いかもしれないなぁ、というものを提案してみようと思います。

何故document.writeがいけないのか

何故なんでしょうね (´・ω・`) ←

This method has very idiosyncratic behavior. In some cases, this method can affect the state of the HTML parser while the parser is running, resulting in a DOM that does not correspond to the source of the document.

HTML living standard

このメソッドは非常に奇異的なふるまいをします。場合によって、パーサー実行中にHTMLパーサーに(良くない)影響を与える可能性があり、その結果、ドキュメントのソースに対応しないDOMが発生します。

あとまぁなんちゃらかんちゃら書いてありますけども、HTML5の理念は「セマンティクスへの集中」です。document.writeはあたかもはじめからそこにあったようなふるまいをしますが、実際には素のhtmlに書かれていないものが書き足されます。DOMに影響(effectiveではなくaffective)を与えるのはそのためですね。

既存のnewマークスクリプトを書き換える

「既存の」というのはFC2サービス内で汎く使われている内容のことです。最初に発案されたのがどなたか存じませんので申し訳ないんですが、長く親しまれてきたコードですからクリエイターさんに感謝ですね。

そのコード内容はFC2独自変数を関数として利用するタイプのものです。これを単純に「innerHTMLやinnreTextへの書き換え」でやろうと思うそれ自体はさほど難しくないんですが、問題は IE対応 です。ここでも足を引っ張るIE。

で、私個人は既に「今後IE対応は致しません」と明言してはいます。けれどもここでは総体的な書き換えの件を書きますので、今回はIEで使えるという点も条件にしています。

document.writeは関数を含むscriptをその場で実行しますので、script要素を書いたその場所にポンと書き出されます。この「その場に(scriptが書いてある位置に)」というのがキモ。IEはdocument.currentScriptが使えません。

IE対応はしない、という場合はinnerHTMLへ置き換えるだけで済みますね。それもひとつの方法だと思います。ともかくJSの場合この書き換えというのが単純な構造ではないものですから、みなさん(コーダー, デザイナー)が苦労されているという現状です。特にIE対応を継続している製作者さんですよね。ここで多少の手助けができればなぁ、と思います。

独自変数を関数として使わない場合

関数というのは function(関数) この中身のことです。ここに独自変数を使うとなると原則 外部ファイル化が難しい んですね。仮に一部を外部ファイル化しても変数の代入はやはり必要ですので中途半端っちゃ中途半端。

ともかく一旦は日付をどこかに書き出さないといけませんので、大抵の着眼点は time要素 ということになるかと思います。ところがtime要素をNEWマーク表示のとっかかりにするという場合には 位置の変更が非常に困難 です。通常time要素は本来の名の通り「記事をUPした(あるいは修正した)日付」が表示されますので、その前後にNEWマーク、という形になります。

ところが「日付横ではなくタイトル横に表示したい」さらには「位置も好き勝手にしたいしもっと装飾したい」という要望が必ず出てくる(笑)
time要素、特にtime要素が持つ datetime属性 というのは比較的強い意味を持つ要素です。正確な日付を伝えるためのマークアップですから、装飾の都合を優先するべきではない と考えます。

「タイトル横に表示させたいから」という理由で

<h2>見出し内容<time datetime="<%topentry_year>-<%topentry_month>-<%topentry_day>T<%topentry_hour>:<%topentry_minute>:<%topentry_second>+09:00"></time></h2>

こういうことは通常はしないというか。構文的にtime要素はフレージングコンテンツですからh要素内への入れ子はOKでエラーではありませんけども、やっぱりセマンティックでは無いような。見出しの中のiframe要素なんかと同じ感覚ですね。こちらも構文的にはOKだけども見出しにiframeなんてのはちょっとおかしいわけで。

なので個人的に「time要素を起点にする」という考え方は消去法で消しました。そうなると次は カスタムデータ属性 というのが順当というか誰もが考え付く結論というか(笑)

というわけでおすすめコード内容

まず以下の点を考慮します。

  • テンプレート内容に左右されない
  • html構文に悩まされない
  • 広汎で使える(記事の日付だけでなくプラグインに応用ができる)

まずテンプレートに左右されないためには jQuery依存を避ける ことが第一ですね。そしてhtmlというのは「この要素にこの要素は入れ子できない」ですとか、厳格な構文上のルールがありますし、あまりにも意味論を壊してしまうようなマークアップも避けたいところです。それには span要素 がやっぱり適しているのではないかな、と思います。

で、先程書いたように「表示の基準になる要素」というのが必ず必要です。documen.writeはそのscriptを書いたその場所に表示されますので非常に楽だったんですが、その他の方法ではそうはいかない。そこで基準になる要素(ターゲットになる要素)を既存のものから選び出すのか、それともnewの文字列を吐き出すための要素を予め設けておくか、という選択になります。今回は後者を選びました。要素は span です。

spanは大概の要素に入れ子が可能ですし、divと同じで 特別な意味を持っていない という特徴があります。装飾に利用するならば「意味を持たない要素」を利用するのが逆にセマンティクス面で貢献(笑)

あとはプラグインにも応用ができること、なんならまとめて新着にもコメントにもNEW、みたいな。

htmlコード

<span data-newdate="<%topentry_year>-<%topentry_month>-<%topentry_day>"></span>

上記を表示したい要素の横に記載(テンプレートhtml内, プラグインhtml内)
こちらは通常記事のNEWマーク (トップページや個別記事)に使う内容です。 例えばh2見出しの <h2> の左横に書けば見出しの「前」に、</h2> の右横に書けば見出しの「後ろ」に表示されます。

見出し横の場合は注意点あり。見出しのCSSに display: inline-block (または inline)を記してdisplay値を変更しないと横に並びません(見出しのdisplay初期値はblockです)
見出しに限らず「横に並ばない」という状態に陥ったときにはその要素のdisplay値をチェックしてください。

あるいはspan要素ですからhx要素の「中」に入れ子することもできます。ただしその場合には見出しが例えば「風邪に効果のある薬膳について new」と赤字部位を含んだものが見出しです。但し数日(指定した日数)経過すれば見た目にもマークアップとしても消えます。

<span data-newdate="<%recent_modified_year>-<%recent_modified_month>-<%recent_modified_day>"></span>

上記は プラグインの新着記事の最終更新日 用です。通常の記事と新着記事などはそれぞれFC2独自変数が違いますので使い分けを行ってください。独自変数以外は同じ内容です。「表示されない」と思ったら独自変数が合致しているかチェック。

<span data-newdate="<%rcomment_year>-<%rcomment_month>-<%rcomment_day>"></span>

上記は 新着コメント 用htmlです。ともかくそれぞれ基本は全く同じでFC2独自変数だけが違います。

JSコード

<script>for(var s=1,c=new Date,n=document.querySelectorAll("[data-newdate]"),i=0;i<n.length;i++){var d=new Date(n[i].dataset.newdate);d.setDate(d.getDate()+s),c<d&&(n[i].classList.add("new"),n[i].innerHTML="new")}</script>

上記内容はJavaScriptですがすぐにコピペできるようにscript要素として掲載しています。記載位置はテンプレートの </body> 直前で構いません。また、外部ファイル化も可能です。その場合は前後の <script></script> を削除した上で .js ファイルとして保存。FC2サーバーにUPしてください。

また、外部ファイル化した場合には async属性 を利用した非同期も可能ですから

<script src="ここにファイルURL" async></script>

こうしてasync指定をしておくと良いですね。外部ファイル化のメリットのひとつと言えます。ただしそんなに大きなJSではありませんのでインライン(テンプレート内に直貼り)でも良いと思います。インラインのメリットは修正が容易な点。修正することがあるとすれば 表示継続期間 だと思います。

表示期間の指定

JSコードの s=1 の部位が 日数 の指定です。「秒」ではなく「日」にしておきましたので、2日継続なら2、3日なら3、と変更が容易だと思います。

2日 以上の指定をおすすめします。直感的に日数で指定できる代わりに秒までの取得はしませんので、単純に日付(day)が切り替わるまでを1日とカウントします。なので深夜にUPするとマーク表示の寿命が短くなります。大抵の閲覧者は「何時何分何秒」までは気にせず「何日」で見るでしょうからこういう形にしました。
* 運営公開コードの場合はUPした時点から秒で正確に取得しますので、正確さを重視する場合は運営コードをご利用ください。

画像を利用する場合

.innerHTML="new" の部位を .innerHTML="<img src='画像URL' alt='代替テキスト'>" に変更。

CSSスタイリングについては各自で行ってください。テンプレートとの兼ね合いもありますのでここでデザインの提示は行いません。

JSコードの大まかな説明

for文で処理を回しています。そして便利な new Date で日付を取得。カスタムデータ属性を持つ要素を取得し、現在日時と指定日数の差分を条件づけします。条件に合致した場合には クラス属性の追加(クラス名 .new)new のテキストの書き出しを行います。

とここで実はひっそりと運営がnewマーク表示のJSコードをUPしてくれていますのでご紹介します。

【ブログ】最近投稿された記事の最終更新時間を表示できるようになりました。

【ブログ】最近投稿された記事の最終更新時間を表示できるようになりました。

平素は、FC2(fc2.com)をご利用いただき、誠にありがとうございます。 この度、FC2ブログ(blog.fc2.comにて、最近投稿された記事の最終更新時間を表示することができるテンプレート変数が追加されました。...

基本的な考え方は同じです。createElementで要素を追加作成するのではなく、先にelementを設けておいて文字列を追加する、という方法。

createElementを避けた理由

既存要素をターゲットにしてnewマーク用の要素を追加作成、という形になると、「ターゲットの前」「ターゲットの後ろ」「ターゲットの中」と、希望がバラけます。その希望に沿ってJSを書き換えないといけないんですね。これは初心者にとっては無理筋だろうと思います。なので無意味な要素だとしても予めspanを設けておく、と。

class属性について

運営のコードはとても効率の良い内容だと思いますので、本来はそれをそのまま使うのがベターかもしれませんが、敢えて私がこうして別のコードを書いているのは

  • 運営の公開コードは IE非対応
  • クラス名が最初から付いている

この2つが難点。一応運営コード内容をザッと説明して弊コードとの違いを確認してください。

(function(span){for(var i=0; i<span.length; i++)new Date(span[i].dataset.modified)-(new Date-0x48190800)>0?span[i].textContent='New':'';})(document.querySelectorAll("span[data-modified]"));

ターゲットは data-xxx のカスタムデータ属性を利用。for文で処理を回し、new Date を利用している点なども同じです。そして日付の差分を16進数で取っています。ちなみにデフォルト値は2週間(長すぎだと思うが(笑))
newのテキスト書き出しには .textContent を利用しており、if文のショートハンドでboolean(ブーリアン)のtrue, false(真偽)の分岐処理を行っています。

まず .textContent がIEでダメ。あと運営コードの場合クラス名を予め用意したspanの方に付けてあります。これも避けたいですよね。

span要素は常に存在していますので、内容が空のままの <span class="xx"></span> であっても、文字列が追加されて <span class="xx">new</span> になっても、クラス名が共通である限り指定したスタイル(装飾)は全てに適用されてしまいます。文字の大きさや色なら問題は目に見えてきませんが、例えば背景色を付けたり、marginやpaddingなどを付けると空のspanにも影響が出ます。なのでif分岐処理の段階でクラス属性を付けるのが妥当だと思います。

まとめ

クラス名は .new にしてあります。document.writeのnewマークスクリプトを愛用されていた方の多くがこのクラス名ではないかと思いますので、これまでのデザインがそのまま引き継がれるはずです。別のクラス名なら変更、無いならば追加。

無意味なspanが常に居座っている、という気持ち悪さはあるかもしれませんね。ただ常時 <span>new</span> とこうしてテキストを含むspanが存在し、分岐で合致しない場合のみ display: none で人間に対しては不可視化する、という逆方向のアプローチよりはるかに良いと思います。都合の悪いものをdisplay操作で消す、という方法はnewマークに限らずなるべく避けるべきです。人間は見えなくともクローラーは見ています。

弊コードの利点としては「継続日数指定が容易」「テンプレート変更時の使いまわしが容易」「記事やコメントなど一括で管理」「過去スクリプトからのスタイル引き継ぎが容易」など。

それにしても運営はやはりもうIEを重視していませんね。だよねー (´・ω・`)
というわけで、newマークスクリプトの提案でした。コードカスタマイズとかCSSとかは自分でやってね ←

最後に弊テンプレート利用者のみなさんに向けて。私このコードこれまでに利用していません ←←←
今後はこれに統一しようかな、と思っています。今決めた (*ノω・*)

Related post

Comments  0

コメントに関する注意事項
  • テンプレートに関するご質問は各テンプレート専用記事でのみ受付致します。また、よくある質問をまとめているページも事前にご参照ください。
  • 専門的なご質問の場合、記事内容と明らかに関連の無い内容はお控えください(雑談の場合はその限りではありません)
  • 第三者が不快と感じる内容や論調でのコメントはお控えください(性的,高圧的,暴力的など)