「Tips」タグアーカイブ

【JavaScript】全ての画像を読み込んでから処理を実行する

要素内にimg要素がある時、全ての画像が読み込まれないと要素の正確な高さを計れなかったりする。そういう時のためのスクリプト。

var imagesLoadListener = (function() {

	var imageCollector = function(expectedCount, completeFn) {
		var receivedCount = 0;
		return function() {
			receivedCount++;
			if(receivedCount === expectedCount) {
				completeFn();
			}
		};
	};

	var imagesLoadListener = function(element, callback) {
		var images = element.querySelectorAll('img');
		if(images === null) {
			callback();
			return;
		}

		//画像の数だけloadListenerが呼ばれたらcallbackが呼ばれる;
		var loadListener = imageCollector(images.length, callback);
		Array.prototype.forEach.call(images, function(img) {
			if(img.complete) {
				loadListener();
			}else {
				img.onload = loadListener;
			}
		});
	};

	return imagesLoadListener;
})();

imagesLoadListenerの第一引数はimg要素を含んでいる要素を指定する。第二引数は画像が全て読み込めた時に実行する処理を指定する。例えば第一引数にdocument.bodyを指定したら、body内にあるimg要素全て読み込むのを待ってから第二引数の処理が実行される。

imagesLoadListener(document.body, function() {alert('Done!!')});

See the Pen imagesLoadListener by shigure (@webkatu) on CodePen.

すでに読み込んでいる画像はimg要素のcomplete属性で判断している。まだ画像が読み込めていないimg要素はloadイベント発火まで待つ。

第一引数の要素内に画像がない場合、第二引数の処理はすぐ実行される。

モダンブラウザのみ対応。

参考: javascript – Can I sync up multiple image onload calls? – Stack Overflow

【JavaScript】注釈・脚注を補完する「AutocompleteAnnotations」

注釈・脚注を補完するスクリプト「AutocompleteAnnotations」を書いた。

ブログを書いていると【1】のように注釈や脚注を付けたい時がある。しかし、一々ハッシュリンク貼ったりa要素のtitle属性に内容を書いたりするのは面倒くさいので自動化する。以下サンプル

//脚注同士のリンクや説明を自動補完;
(function autocompleteAnnotations() {

	var range = document.createRange();

	//注釈側の処理;
	(function() {
		//注釈はannotationsというclass属性を持っている;
		var annotations = document.getElementsByClassName('annotations');
		var modelA = document.createElement('a');
		modelA.id = 'annotation';
		modelA.href = '#footnote';
		for(var i = 0; i < annotations.length; i++) {
			var a = modelA.cloneNode(true);
			a.id += i;
			a.href += i;
			//注釈文字をa要素で囲む;
			range.selectNodeContents(annotations[i]);
			range.surroundContents(a);
		}
	})();

	//脚注側の処理;
	(function() {
		//脚注はfootnotesというclass属性を持っている;
		var footnotes = document.getElementsByClassName('footnotes');
		var modelA = document.createElement('a');
		modelA.id = 'footnote';
		modelA.href = '#annotation';
		for(var i = 0; i < footnotes.length; i++) {
			//脚注を指している注釈側のtitle属性に同じ説明を加える;
			var annotation = document.getElementById('annotation' + i);
			annotation.title = footnotes[i].textContent;
			var a = modelA.cloneNode(true);
			a.id += i;
			a.href += i;
			a.textContent = annotation.textContent;
			range.setStart(footnotes[i], 0);
			range.insertNode(document.createTextNode(': '));
			range.insertNode(a);
		}
	})();

	range.detach();
})();

See the Pen AutocompleteAnnotations by shigure (@webkatu) on CodePen.

使い方

「autocompleteAnnotations.js」をdefer属性をつけて呼び出し、注釈部分の要素に「class=”annotations”」という属性をつけて、説明部分の要素に「class=”footnotes”」という属性をつければOK。

<head>
	<!-- defer属性をつけてscriptを呼び出す -->
	<script defer src="autocompleteAnnotations.js"></script>
</head>
<body>
	<!-- 注釈部分を"annotations"というclass名の要素で囲む -->
	<p>hogehoge<span class="annotations">【1】</span></p>
	<p>fugafuga</p>
	<!-- 説明部分(脚注)を"footnotes"というクラス名の要素で囲む -->
	<p class="footnotes">piyopiyo</p>
</body>
<head>
	<script defer src="autocompleteAnnotations.js"></script>
</head>
<body>
	<p>hogehoge<span class="annotations"><a id="annotation0" href="#footnote0" title="piyopiyo">【1】</a></span></p>
	<p>fugafuga</p>
	<p class="footnotes"><a id="footnote0" href="#annotation0">【1】</a>: piyopiyo</p>
</body>

GitHub

【JavaScript】script要素<script>を動的に実行する

innerHTMLでscript要素を追加したり、既に実行されたscript要素をcloneNodeでコピーしてそれを追加しても処理は実行されない。script要素を実行するには、createElementで新たにscript要素を作り、appendChildで追加する必要がある。

例えば、Ajaxなどでhtmlを書き換える時、同時にscript要素にある処理も実行したい場合、新たにscript要素を作ってそこに実行したい処理をコピーしなければいけない。そういう時に使えるスクリプトを書いた。

function cloneScript(element) {
	var script = document.createElement('script');
	var attrs = element.attributes;
	for(var i = 0, len = attrs.length; i < len; i++) {
		var attr = attrs[i];
		script.setAttribute(attr.name, attr.value);
	}
	script.innerHTML = element.innerHTML;
	return script;
}

引数に指定されたscript要素の属性と処理内容をコピーした新しく作られたscript要素を返すので、src属性で外部jsファイルを指定しているscript要素や、script要素内に直接処理の書かれているscript要素どちらでも問題ないはず。

例えば以下のようなscript要素を再度実行する場合

<script class="origin">alert('hello');<script>
var origin = document.querySelector('.origin');
document.body.appendChild(cloneScript(origin)); //'hello'とalertが鳴る;

こんな感じで使える。