SEOに強いフレームワーク開発

Googleが提唱しているSEOの強いサイトの特徴は、「速い」と「レスポンシブ」だ。
これは、明らかにスマートフォンユーザーの利便性を追求して、モバイル端末で見易いページを優先するというGoogleの会社の姿勢でもある事がよくわかる。
確かに、何かを検索する場面でスマートフォンを使う事が多い事は明確だが、当たり前だがPC表示を疎かにしていいわけではない。
マルチデバイス、マルチブラウザ、そのユーザーの環境に合わせたWEBサイトを作らなくてはいけないというのが、コンテンツメーカーに課せられた使命であると言っても過言ではないでしょう。
あまり知られていないSEOに強いサイトの特徴
とあるメディア・コンテンツの製作を担当している友人から、面白い話を聞きました。
1ページを複数ページに分割して、PV数をかさ増しするよりも、ページコンテンツを遷移する時に、history移動させずに、ajaxでソースコードを読んできて、ページが切り替わらないようなサイトを構築できると、Googleのクローラーは、ページ滞在時間が多いページは優良コンテンツとみなし、SEO順位が上昇する。
非常に興味深い話でその根拠を聞いてみた所、とあるサイトをスクラッチで作った時に、AJAX読み込み方式にすることで、メジャーなHOTワードだったにも関わらず、そのキーワードで検索順位が1位になることができたとのこと。
もちろん他の要因もあるかと思ったのだが、ページは極めてシンプルなため、ページ滞在時間でSEOが向上するのがよく理解できた。
新聞記事ページなどで、ページ分割しているコンテンツサイトをよく見かけるが、ページを分けてPVを向上させるよりも、滞在時間によりSEOを向上させて、結果的にPVも増えるという構造が望ましいという事。
もちろん、Googleが検索アルゴリズムを変更したら元も子もないのだが、今の時点で有効であれば、これを考慮したフレームワークを作っておけば、以後自分の構築するWEBサイトはSEOに強いサイトが作ることが可能になるという理屈だ。
プログラム構築の事前知識
そこで、今回は、ページの読み込みをAJAXを使ってWEBサイトのソースを取ってきて、その中の特定エレメントを差し替えるというシンプルな構成にしてみた。
ただし、このフレームワークで使う上での注意点は以下のとおりである。
・サイト全体でメニューやコンテンツ部分が同じ構成である事。
・ID=”contents”の内部ソースコードを入れ替える方式
※このID値を持っていなければ通常遷移になる。
・次ページに切り替わった後、JSを起動するようなページは動作しない
※JS実行をロジックに入れていないため
上記の条件があるため、一般的なブログシステムレベルを想定している。
少なくともwordpressよりは早くページ遷移できるのではないかと考えている。
そして、このプログラムは、以前とある会社で研究開発したプログラムを少し改良して作っている。
WebPageAccerelator
このプログラムは、ページ読み込みをいかに高速化できるかを研究しているので、そのページ読み込み部分のロジックを拝借した。
ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
/** * Title : LinkSourceGetter * Auther : yugeta@showcase-tv.com * Date : 2017.03.30 * Version : 1.0 / Body-change (2017.03.30) * Version : 1.1 / history-back , page-title (2017.07.13) */ ;(function(){ /** * start */ var $$ = function(){ // Debug-Message-- console.log("WPA-LinkSourceGetter : Start "); if(window.flgLinkSourceGetter == "set"){ console.log("WPA-LinkSourceGetter : set-over"); return; } //$$.dom.__construct(); this.setMutationObserver(); // page load-status check if(document.readyState === "complete"){ this.setPageLoaded(); } else{ this.setEvent(window , "DOMContentLoaded" , $$.prototype.setPageLoaded); } // URL History proc this.setEvent(window , "popstate" , $$.prototype.setHistoryBack); }; $$.prototype.urls = []; /** * loaded */ $$.prototype.setPageLoaded = function(){ var links = document.links; for(var i=0; i<links.length; i++){ $$.prototype.setLinkTags(links[i]); } // flg-set window.flgLinkSourceGetter = "set"; // URL-cache if($$.prototype.urls.length === 0 || $$.prototype.urls.indexOf(location.href) === -1){ $$.prototype.urls.push(location.href); } }; /** * DOM-event-proccess */ $$.prototype.setMutationObserver = function(){ var target = document.getElementsByTagName("html"); if(!target.length){return} var mo = new MutationObserver(function(mutationRecords){ for(var i=0; i<mutationRecords.length; i++){ for(var j=0; j<mutationRecords[i].addedNodes.length; j++){ // Image-Tag-Proc $$.prototype.setLinkTags(mutationRecords[i].addedNodes[j]); } } }); // Event-Set mo.observe(target[0] , {childList:true , subtree:true}); }; /** * set-link-tags */ $$.prototype.setLinkTags = function(elm){ // checc-elm if(!elm || elm.nodeType != 1){return} // check-flg if(elm.getAttribute("data-link-flg") == "set"){return} // check-tag if(elm.tagName != "A"){return} // check-href if(!elm.href){return} // domain-check var hrefs = elm.href.split("/"); if(hrefs.length < 2){return} if(hrefs[2] != location.host){return} // set-onclick elm.onclick = $$.prototype.procLinkClick; // set-flg elm.setAttribute("data-link-flg","set"); }; /** * proc-link-click */ $$.prototype.procLinkClick = function(){ var ajax = new $$ajax; ajax.set({ url:this.href, method:"GET", async:"true", onSuccess:$$.prototype.setSourceChange }); return false; }; /** * getLinkSource */ $$.prototype.setSourceChange = function(res){ // System console.log("WPA-LinkSourceGetter : [click] : " + this.url); // SetDOM var dom = document.createElement("html"); dom.innerHTML = res; // contentsチェック var elms = dom.getElementsByTagName("*"); var contents=null; for(var i=0; i<elms.length; i++){ if(elms[i].getAttribute("id") === "contents"){ contents = elms[i]; break; } } if(contents === null){ location.href = this.url; return; } // Address-URL change history.pushState(null,null,this.url); /** * headとbodyのソースを入れ替え定義(script対応版) */ var rootNode = document.getElementsByTagName("html")[0]; var domHTML = dom.getElementsByTagName("html")[0]; var domHead = dom.getElementsByTagName("head")[0]; var domBody = dom.getElementsByTagName("body")[0]; var docHead = document.getElementsByTagName("head")[0]; var docBody = document.getElementsByTagName("body")[0]; // // Body入れ替え // rootNode.replaceChild(domBody , docBody); // Contents入れ替え document.getElementById("contents").innerHTML = contents.innerHTML; // Title入れ替え document.title = dom.getElementsByTagName("title")[0].textContent; //スクロールtop window.scrollTo(0,0); // 読み込み後にリンク設定(ページ初期設定) $$.prototype.setPageLoaded(); }; /** * Ajax * $$.prototype.ajax.set({ * url:"", // "http://***" * method:"POST", // POST or GET * async:true, // true or false * data:{}, // Object * query:{}, // Object * querys:[] // Array * }); */ var $$ajax = function(){}; $$ajax.prototype.dataOption = { url:"", query:{}, // same-key Nothing querys:[], // same-key OK data:{}, // ETC-data event受渡用 async:"true", // [trye:非同期 false:同期] method:"POST", // [POST / GET] type:"application/x-www-form-urlencoded", // [text/javascript]... onSuccess:function(res){}, onError:function(res){} }; $$ajax.prototype.option = {}; $$ajax.prototype.createHttpRequest = function(){ //Win ie用 if(window.ActiveXObject){ //MSXML2以降用; try{return new ActiveXObject("Msxml2.XMLHTTP")} catch(e){ //旧MSXML用; try{return new ActiveXObject("Microsoft.XMLHTTP")} catch(e2){return null} } } //Win ie以外のXMLHttpRequestオブジェクト実装ブラウザ用; else if(window.XMLHttpRequest){return new XMLHttpRequest()} else{return null} }; // XMLHttpRequestオブジェクト生成 $$ajax.prototype.set = function(options){ if(!options){return} var ajax = new $$ajax; var httpoj = $$ajax.prototype.createHttpRequest(); if(!httpoj){return;} // open メソッド; var option = ajax.setOption(options); // 実行 httpoj.open( option.method , option.url , option.async ); // type httpoj.setRequestHeader('Content-Type', option.type); // onload-check httpoj.onreadystatechange = function(){ //readyState値は4で受信完了; if (this.readyState==4){ //コールバック option.onSuccess(this.responseText); } }; //query整形 var data = ajax.setQuery(option); //send メソッド if(data.length){ httpoj.send(data.join("&")); } else{ httpoj.send(); } }; $$ajax.prototype.setOption = function(options){ var option = {}; for(var i in this.dataOption){ if(typeof options[i] != "undefined"){ option[i] = options[i]; } else{ option[i] = this.dataOption[i]; } } return option; }; $$ajax.prototype.setQuery = function(option){ var data = []; if(typeof option.query != "undefined"){ for(var i in option.query){ data.push(i+"="+encodeURIComponent(option.query[i])); } } if(typeof option.querys != "undefined"){ for(var i=0;i<option.querys.length;i++){ if(typeof option.querys[i] == "Array"){ data.push(option.querys[i][0]+"="+encodeURIComponent(option.querys[i][1])); } else{ var sp = option.querys[i].split("="); data.push(sp[0]+"="+encodeURIComponent(sp[1])); } } } return data; }; /** * イベント処理(マルチブラウザ対応) * Event-Set * param @ target : Target-element * param @ mode : mode ["onload"->"load" , "onclick"->"click"] * param @ func : function **/ $$.prototype.setEvent = function(target, mode, func){ //other Browser if (target.addEventListener){target.addEventListener(mode, func, false)} else{target.attachEvent('on' + mode, function(){func.call(target , window.event)})} }; /** * History.back対応 */ $$.prototype.setHistoryBack = function(){ // 遷移したURLリストに存在するかの確認 if($$.prototype.urls.indexOf(location.href) !== -1){ $$.prototype.setHrefSource(location.href); } }; $$.prototype.setHrefSource = function(url){ if(!url){return} var ajax = new $$ajax; ajax.set({ url:url, method:"GET", async:"true", onSuccess:function(res){ // SetDOM var dom = document.createElement("html"); dom.innerHTML = res; // contentsチェック var elms = dom.getElementsByTagName("*"); var contents=null; for(var i=0; i<elms.length; i++){ if(elms[i].getAttribute("id") === "contents"){ contents = elms[i]; break; } } if(contents === null){ location.href = this.url; return; } /** * headとbodyのソースを入れ替え定義(script対応版) */ var rootNode = document.getElementsByTagName("html")[0]; var domHTML = dom.getElementsByTagName("html")[0]; var domHead = dom.getElementsByTagName("head")[0]; var domBody = dom.getElementsByTagName("body")[0]; var docHead = document.getElementsByTagName("head")[0]; var docBody = document.getElementsByTagName("body")[0]; // // Body入れ替え // rootNode.replaceChild(domBody , docBody); // Contents入れ替え document.getElementById("contents").innerHTML = contents.innerHTML; // 内部Script実行 // Title入れ替え document.title = dom.getElementsByTagName("title")[0].textContent; //スクロールtop window.scrollTo(0,0); // 読み込み後にリンク設定(ページ初期設定) $$.prototype.setPageLoaded(); // console.log("WPA-LinkSourceGetter : [history] : " + this.url); } }); }; new $$(); window.LinkSourceGetter = $$; })(); |
サンプルサイト
サンプルページには上部に4つのリンクがあり、一番右のみ、contentsエレメントを含んでいない為、通常遷移になる。
ソレ以外の3つに関しては、contentsのみの差し替えになるし、ページ内の各種モジュールを読み込まないのでスピードも早い。
使い方
至って簡単で、上記axcel.jsをページのヘッダ部分にscriptタグ1行(ファイル読み込み)で追記するだけでいい。
id=”contents”部分でなくて別のエレメントが希望の場合は、ソースコードの133行目、160m行目と312行目、336行目を書き換えてあげるとお望みの場所を切り替えることが可能になる。
まだまとまりきれていないコードなので、整理もできるが、とりあえず、効果を見るにはいいかもしれないので、これをwordpressに実装してみたり、自分でフレームワークを作ってみてもいいかもしれない。
今回は高速読み込みとページ遷移をコントロールできるモジュールのみを開発したのだが、これを実装できる便利で使いやすいフレームワークを開発中なので、完成したら、発表したいと思います。