[Javascript] ページ内のキーワードをハイライトさせる「HighLight.js」ライブラリ

2019年6月12日

Javascript テクノロジー プログラミング

人類の偉大な発明の一つに「インターネット」がありますが、世界規模での公開型データベースと言えるような情報共有システムで、誰もがスマホでググれば、欲しい情報はなんでも手に入る時代です。 もはや、あれなんだっけ?と考えるぐらいならブラウザで検索した方が無駄な時間を使わないし、人に聞くぐらいなら入力した方が早いので、検索することに命を掛けているような人も見かけてなんだか滑稽に見えるのが面白く感じますが、未だに昨日の晩御飯も思い出せない僕としては、検索も今後もっと便利になる予感がしています。 検索サイトや辞書サイトなどで、任意のキーワードで検索した時に、ページ内のキーワードをマーキングしてくれるハイライト機能を見かけることがありますが、これを自社サイトなどで簡単に使える機能をjavascriptで作ってみました。 ブラウザの機能拡張などに入れると、サイト内検索まではいかないが、ブラウザ検索の簡易版を機能として作ることも可能です。

ソースコード

※出展wikipedia <!doctype html> <html> <head> <meta charset="utf-8"> <title>HighLight.js</title> <link rel="stylesheet" href="highlight.css"> <script src="highlight.js"></script> </head> <body> <input type="" id="highlight"> <h1>株式会社(<a href="https://ja.wikipedia.org/wiki/%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BE">wikipedia</a>より転機)</h1> <h2>国際的に見た株式会社の一般的特質</h2> <pre> ヨーロッパ諸国、アメリカ、日本の会社法を比較研究したKraakman et al. (2004) は、株式会社の特質は、(1)法人格、(2)出資者(株主)の有限責任、(3)持分の自由譲渡性、(4)取締役会への経営権の委任(所有と経営の分離)、(5)出資者(株主)による所有の5点にあるとし、この五つを兼ね備えたものが株式会社の基本形であるとする。そして、市場経済の国においては、ほとんどすべての大規模企業がこれら5点の特徴を備えていると指摘している[2]。日本の株式会社の中のいわゆる公開会社、ドイツの株式会社(AG)、フランスの株式会社(SA)、アメリカのコーポレーションの中の公開会社(英語版)、イギリスの株式有限責任会社(英語版)の中の株式有限責任公開会社(英語版)はこれに当たる。 その一方で、各国とも、これらに類似しつつも、(3)の特徴を有しない閉鎖型の会社形態が何らかの形で規定されていることが通常である。大陸法圏においては株式会社とは別個の企業形態として有限会社が立法されることが多く、かつての日本の有限会社、ドイツの有限会社(GmbH)、フランスの有限会社(SARL)などがある。一方、現在の日本や英米法圏などでは、株式会社の一種として立法されており、日本の株式会社の中の非公開会社、アメリカのコーポレーションの中の閉鎖会社(close corporation)、イギリスの株式有限責任会社(company limited by shares)の中の株式有限責任私会社(private company limited by shares)などがある。[3]。 なお、国によっては(2)の例外として、株主と並ぶ無限責任社員の存在を認める企業形態を認めるものもある。フランス、ドイツ、かつての日本など、大陸法圏で認められる株式合資会社が典型であるが、英米法圏においても、英領ヴァージン諸島の株式発行を授権された無限責任会社(unlimited company that is authorised to issue shares)などがある。 </pre> <h2>法人格</h2> <pre> 会社は、自然人と同様、それ自体が権利・義務の主体となることができる権利能力を有している。すなわち、会社はその構成員とは区別された法人格 (legal entity) を有する[4]。これにより、会社は自己の名において事業を行い、財産を取得・処分し、契約を締結し、借入れを行うことができる。そして、経営者や株主に対して債権を有する債権者も、別人格である会社の財産に対しては債権を行使することができない[5]。 </pre> <h2>出資者の有限責任</h2> <pre> 法人格のコロラリーである。会社に対する債権者(会社債権者)は、会社の財産に対してのみ債権を行使することができ、株主(出資者)の財産に対して債権を行使することはできないという原則を、株主(出資者)の有限責任という。すなわち、株主の責任は、引き受けた株式について出資の履行を行ったことで果たされており、会社の債務について会社債権者に対して責任を負わない[6]。法人格が、会社の財産を株主の債権者から守るものであるのに対し、有限責任は、株主の財産を会社の債権者から守るものであるといえる[7]。 これは、出資をしようとする者にとってのリスクを限定することによって、多数の出資者から広く出資を集めることを可能にするためのものである。また、有限責任によって出資者と会社債権者との間のリスクの分配が明確になるため、出資持分(株式)の譲渡が容易になり、会社債権者との取引も容易になる[8]。有限責任を認めることによって、会社がある事業を行うために子会社を設立して、事業失敗による損失のリスクを限定することも可能である[9]。 かつては、出資者は会社の債務について無限責任を負うこととされていたが、今日では有限責任は普遍的な制度となっている[10]。もっとも、日本の合名会社、合資会社においては、全部又は一部の社員が会社の債務について無限責任を負う[11]。 有限責任の下では、会社債権者にとっては会社の財産だけが責任財産となることから、会社債権者の保護も会社法の課題となる[12]。 </pre> <h2>株式の自由譲渡性</h2> <pre> 株主が、その有する株式(出資持分)を自由に譲渡することができることを、株式の自由譲渡性という[13]。 これは、株主がいつでも株式を譲渡して会社関係から離脱することができるようにすることによって、相互に信頼関係のない多数の者から広く資本を集めることができるようにする仕組みである。株式会社では、責任財産を会社に確保するために、出資の払戻しをすることが原則として認められていないため、株主にとっては株式の譲渡は投下資本回収のための重要な手段である[14]。 もっとも、多くの中小企業のように人的関係が重要な意味を持つ会社では、自由譲渡性を認めると人的関係を維持することが難しくなることから、各国の会社法は、一定の会社について株式の譲渡を制限することを認めている。株式の譲渡を制限する会社については、日本の譲渡制限株式のように一般的な会社法の中の特則として設けられている場合もあれば、かつての日本やヨーロッパ大陸諸国のように閉鎖会社に関する独立の制定法による企業形態(有限会社)が設けられている場合もある[15]。 </pre> <h2>所有と経営の分離</h2> <pre> 会社において、株主は直接経営を行わず、経営者(取締役会など)に経営権を集中することを、所有と経営の分離といい、これは多数の株主を有する大企業では普遍的に見られる特質である[16]。 このような傾向は、歴史的に会社が大規模化し、多くの株主から資金を集めなければならなくなった結果、株主が直接経営を行うことが難しくなり、専門的経営者に経営が委ねられるようになったことによる。アメリカでは20世紀初めころから所有と経営の分離が進んだ[17]。また、所有と経営を分離することにより、会社と取引をしようとする第三者にとっては、誰が権限を有するかが分かりやすいという利点もある[16]。 各国とも、株主による投票で取締役が選ばれ、その取締役で構成される取締役会 (board of directors) が、経営上の意思決定及び業務執行の監督を行うというのが典型的な制度である。一方、日々の業務執行は、日本では代表取締役、アメリカでは執行役員 (officer) が行うのが通常である[18]。 </pre> <h2>株主による所有</h2> <pre> (1)株主が、会社を最終的にコントロールする権限(取締役を選任し、会社の運営上重要な事項を承認する権限)を有すること、(2)会社の純利益は株主に帰属することを指して、株主が会社を所有するという[19]。この意味で、会社は、組合、匿名組合、信託などと同様、出資者が所有する共同事業形態であるといえる[20][注釈 1]。もちろん、会社の純利益が株主に帰属する反面、会社に損失が出た場合も、株主は(配当を受け取れない、あるいは株価の下落という形で)そのリスクを負担する[21]。 なお、上記のような法学的な説明とはやや異なる意味で、会社の目的は、株主の利益を最大化することにあるという立場(株主主権論)から「会社は株主のものである」という主張がされることがある[22]。これに対しては、「会社はコア従業員(長期的に会社に関わる従業員)のものである」という従業員主権論や、「会社はステークホルダー(株主、従業員、顧客、取引先、地域社会といった利害関係者すべて)のものである」という主張もされている[23]。このような会社は誰のものかという議論は、経営やコーポレート・ガバナンス(企業統治。後述)の重点をどこに置くかについての議論であるといえる[24]。また、ステークホルダー型コーポレート・ガバナンスと関連して、会社は地域の利益や雇用、環境を守る責任があるという企業の社会的責任(CSR) も主張されている[25]。 ただし、例えば株主主権論の立場に立つとしても、従業員等のステークホルダーに正当な対価を支払わなければ株主の利益を生み出すことができないというように、「会社は誰のものか」という議論を、専らある者の利益のために会社を経営すべきであるという主張として理解することには実益があると指摘されている[26]。 </pre> <hr> <input type="text" value="アメリカ"> <input type="text" value="ヨーロッパ"> <input type="text" value="コントロール"> <hr> <span class="a">abc<span class="b">def</span>ghijk</span> </body> </html> html,body{ width:100%; height:100%; } span.highlight{ background-color:#FF0; } pre{ white-space:pre-wrap; width:95%; word-break:break-all; } ;$$highlight = (function(){ var $$event = function(target, mode, func){ if (typeof target.addEventListener !== "undefined"){target.addEventListener(mode, func, false);} else if(typeof target.attachEvent !== "undefined"){target.attachEvent('on' + mode, function(){func.call(target , window.event)});} }; var $$urlinfo = function(uri){ uri = (uri) ? uri : location.href; var urls_hash = uri.split("#"); var urls_query = urls_hash[0].split("?"); var sp = urls_query[0].split("/"); var data={ uri : uri, url : sp.join("/"), dir : sp.slice(0 , sp.length-1).join("/") +"/", file : sp.pop(), domain : sp[2], protocol : sp[0].replace(":",""), query : (urls_query[1])?(function(urls_query){ var data={}; var sp = urls_query.split("&"); for(var i=0;i<sp .length;i++){ var kv = sp[i].split("="); if(!kv[0]){continue} data[kv[0]]=kv[1]; } return data; })(urls_query[1]) : [] , hash : (urls_hash[1]) ? urls_hash[1] : "" }; return data; }; var $$init = function(){ switch(document.readyState){ case "complete" : new $$; break; case "interactive" : $$event(window , "DOMContentLoaded" , (function(e){new $$}).bind(this)); break; default : $$event(window , "load" , (function(e){new $$}).bind(this)); break; } }; var $$ = function(options){ var urlinfo = $$urlinfo(); var word = (typeof urlinfo.query.highlight !== "undefined" && urlinfo.query.highlight) ? decodeURI(urlinfo.query.highlight) : ""; this.setHighlight(word); this.setInputEvent(this.options.form_selector); this.setInputValue(word , this.options.form_selector); }; $$.prototype.options = { highlight_class : "highlight", form_selector : "#highlight" }; $$.prototype.setInputEvent = function(selector){ var target = document.querySelector(selector); if(!target){return;} $$event(target , "blur" , (function(e){this.changeHighlight(e)}).bind(this)); }; $$.prototype.setInputValue = function(word , selector){ if(word === "" || word === null || word === undefined || word === false){return;} var highlightInput = document.querySelector(selector); if(!highlightInput){return;} highlightInput.value = word; }; $$.prototype.createHighlightTag = function(word){ return "<span class='"+ this.options.highlight_class +"'>"+word+"</span>"; }; $$.prototype.createHighlightElement = function(word){ var span = document.createElement("span"); span.className = this.options.highlight_class; span.textContent = word; return span; }; $$.prototype.setHighlight = function(word , elm){ if(word === "" || word === null || word === undefined || word === false){return;} if(!elm){ this.setHighlight(word , document.body); } else{ var nodes = elm.childNodes; var count = nodes.length; for(var i=0; i<count; i++){ // 1 : element if(nodes[i].nodeType === 1){ this.setHighlight(word , nodes[i]); } // 3 : text else if(nodes[i].nodeType === 3){ var txt = nodes[i].textContent; if(txt.indexOf(word) === -1){continue;} var texts = txt.split(word); var docfrag = document.createDocumentFragment(); for(var j=0; j<texts.length-1; j++){ var tn = document.createTextNode(texts[j]); if(j === 0){ tn.baseText = txt; } else{ tn.baseText = ""; } docfrag.appendChild(tn); docfrag.appendChild(this.createHighlightElement(word)); } var tn = document.createTextNode(texts[texts.length-1]); tn.baseText = ""; docfrag.appendChild(tn); nodes[i].parentNode.replaceChild(docfrag , nodes[i]); } } } }; $$.prototype.clearHighlights = function(){ var elms = document.querySelectorAll("." + this.options.highlight_class); var count = elms.length; for(var i=count-1; i>=0; i--){ var word = elms[i].textContent; var tn = document.createTextNode(word); tn.baseText = ""; elms[i].parentNode.replaceChild(tn , elms[i]); } this.linkTextNode(); }; $$.prototype.linkTextNode = function(elm){ elm = (elm) ? elm : document.body; var nodes = elm.childNodes; if(!nodes){return;} var count = nodes.length; for(var i=count-1; i>=0; i--){ // 1 : element if(nodes[i].nodeType === 1){ this.linkTextNode(nodes[i]); } // 3 : text else if(nodes[i].nodeType === 3){ if(typeof nodes[i].baseText === "undefined"){continue;} if(nodes[i].baseText){ nodes[i].textContent = nodes[i].baseText; } else{ nodes[i].parentNode.removeChild(nodes[i]); } } } }; $$.prototype.changeHighlight = function(e){ var target = e.currentTarget; if(!target){return;} this.clearHighlights(); var word = target.value; this.setHighlight(word); }; $$init(); return $$; })();

解説

javascriptライブラリで動作する仕組みなので、サイトに組み込みやすいようにしています。 cssは、ハイライトさせるためなので、重要なのは以下の箇所のみです。 span.highlight{ background-color:#FF0; } 初回のバージョンは、URLクエリのhighlight=***に対してページ読み込み時にハイライトされるんですが、その後ページ内の入力フォームに文字をいれると、blurタイミングで、ハイライトが切り替わるようにしています。 初回バージョンにはオプションを付けていないので、jsコードを直接書き換えて使ってもらうしか無いのですが、使いたい要望が多かった場合、汎用性を考慮したライブラリ形式にアップデートしようと思います。 特に苦労した点としては、ハイライトを元の状態に戻す方法が、手作業で行うしか無かったので、textNodeのnode要素に対してbaseTextという文字列を埋め込んで、対応しています。 どういうことかというと、以下のような文字列が合った場合に、ハイライトすると、ノードが分割されてしまいます。 ABCDEFG ↓(CDをハイライト) AB<span>CD</span>EFG これをそのままtextNodeにもどすと、 "AB" + "CD" + "EFG" という風に、3つのノードに分割されてしまうのを、1つのノードに戻す処理がかなり手こずりましたが、なんとか実装に成功しました。

サンプル

入力フォームにテキストを入れると、ハイライトされます。

See the Pen highlight.js by YugetaKoji (@geta1972) on CodePen.

Github

ソースコードが欲しい人はこちらからどうぞ。 https://github.com/yugeta/highlight-js

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ