ツールチップを自前で実装
したのでメモっておく。
つーかはてなブログ、CSSもJSも記事内で書けるのかスゲー。
ということで実装サンプルも置いておく。
↓実装サンプルここから
================================
ここにマウスを乗せるとピョッと出てくる
================================
↑実装サンプルここまで
ツールチップは結構いろいろな方法で実現できるようですが(末尾の参考文献を参照のこと)
今回は以下のような感じに落ち着きました。
<HTML>
<span class="tooltip-event" data-tooltip="ピョッと出てくるやつの中身"> ここにマウスを乗せるとピョッと出てくる </span>
特に変わったところはなし。
spanでなく他のタグ(divでもaでもテーブルでも)に入れ替えてもOK。
ツールチップの中身は改行入っててもOK。
ものによるけどタグもOK。
逆に言えば、タグを文字列として出したい場合はエスケープが必要になる。
<CSS>
.tooltip-body { background: #666; color: #fff; width: 400px; padding: 10px; position: absolute; text-align: left; z-index: 0; display: none; }
ツールチップ部分の見た目。
基本的に好きに変えられる。
周りのコンテンツに合わせて、z-indexは調整する必要あり。
また、フェードインに使うのでデフォルトは「display: none;」にしておく。
<jQuery>
$(".tooltip-event").hover( function(e) { var text = $(this).attr("data-tooltip"); if (text != ""){ var spantag = $("<span></span>", {id:"activetooltip", "class": "tooltip-body"}); spantag.html(text.replace(/\r\n|\r|\n/g, "<br>")); $("body")[0].appendChild(spantag[0]); $("#activetooltip").css( { 'top' : $(document).scrollTop() + e.clientY + 10, 'left': $(document).scrollLeft() + e.clientX + 10, } ); setTimeout(function(){ $("#activetooltip").animate({opacity: 'show'}, {duration: 200, easing: 'swing'}); }, 300); } }, function() { $("#activetooltip").remove(); } );
本体。
「tooltip-event」クラスを持つ要素にマウスが乗っかると動く。
以下に抜粋したところが、乗っかったときの処理。
// ツールチップの中身に入る文字列を抜き出してくる var text = $(this).attr("data-tooltip"); // 抜き出した文字列が空でないときだけツールチップを出す if (text != ""){ // ツールチップ本体となるspanタグを生成 var spantag = $("<span></span>", {id:"activetooltip", "class": "tooltip-body"}); // spanタグに、改行コードをbrタグに変換した文字列を突っ込む spantag.html(text.replace(/\r\n|\r|\n/g, "<br>")); // bodyタグの末尾に加える $("body")[0].appendChild(spantag[0]); // 表示位置を調整する(スクロール位置+マウス位置+右と下に10px) $("#activetooltip").css( { 'top' : $(document).scrollTop() + e.clientY + 10, 'left': $(document).scrollLeft() + e.clientX + 10, } ); // 300ミリ秒後に、200ミリ秒かけてフェードイン setTimeout(function(){ $("#activetooltip").animate({opacity: 'show'}, {duration: 200, easing: 'swing'}); }, 300); }
抜粋するほどでもない気はしますが、
マウスが外れたときの処理がこちら。
// activetooltipというIDの要素を削除 $("#activetooltip").remove();
参考文献
- jQuery の animate で、透明⇔不透明のアニメーション動作
- jQueryでブラウザのスクロール値を取得してみる | BlackFlag
- JavaScript - javascriptで変数にタグを追加するとそのまま文字列として表示されてしまう(79783)|teratail
- 【リンク】jQueryでツールチップ | Webデザインラボ
- コピペでできる!CSSとhtmlのみで作るツールチップ | copypet.jp|パーツで探す、web制作に使えるコピペサイト。
- jQueryのhover()を活用したマウス操作まとめ! | 侍エンジニア塾ブログ | プログラミング入門者向け学習情報サイト
- マウスに追従する吹き出しを表示 | 私的雑録
setIntervalの挙動で勘違いしていたこと
setIntervalを単なる時間区切りループ的なものと認識していたらつまづいた話。(初心者丸出し)
※コードはjQueryで書いています
以下のコード、
「start」→「roop:0」→…「roop:4」→「end」
と動くのかと思ったら、
「start」→「end」→「roop:0」→…「roop:4」
だった。
$(function(){ console.log("start"); var i = 0; var timer = setInterval(function(){ console.log("roop:" + i); i ++; if (i >= 5){ clearInterval(timer); } }, 500); console.log("end"); });
setIntervalはざっくり言うと「インターバルタイマーをセットする処理」。
タイマーをセットするだけのおしごとなので、
セットし終わったらその実行がどうとか関係なく次の処理に移ります。
うーんなるほど…。
ループ的に使いたければ、clearIntervalしている部分にその後実行したい処理を書く必要があるわけですね。
$(function(){ console.log("start"); var i = 0; var timer = setInterval(function(){ console.log("roop:" + i); i ++; if (i >= 5){ console.log("end"); clearInterval(timer); } }, 500); });
新しいウインドウを開き、そこに値を送信する
まずは結論
<HTML:親ウインドウ>
<a href="#" onclick="pushValue();">ウインドウを開いて値を送信する</a>
<HTML:子ウインドウ>
<input type="hidden" id="testid" value="">
<jQuery>
var obj; // 開いたウインドウを扱うためのオブジェクト変数 function pushValue(){ // ウインドウオブジェクトが存在しない場合・閉じられている場合は新たに開く if (!obj || obj.closed){ // ウインドウを開く(/childwindow.htmlを、横500px×縦500pxで) obj = window.open("/childwindow.html", "child", "width=500, height=500"); // 書き換えたい要素が存在すれば(=書き換え部分が読み込まれれば)書き換えを行う // 500ミリ秒ごとにリトライ var timer = setInterval(function(){ if ($("#testid", obj.document) != undefined){ sendValue(); clearInterval(timer); } }, 500); } // ウインドウが既に開かれている場合は要素の書き換えを行う else{ sendValue(); } } function sendValue(){ $("#testid", obj.document).val("testvalue"); }
と、こんな感じでの実装となりました。
以下はjQuery部分のメイキング。
ウインドウを開く部分
「window.open(開くHTMLのパス, 名前, スタイル)」で
別ウインドウを開くことができます。
<jQuery>
function pushValue(){ window.open("/childwindow.html", "child", "width=500, height=500"); }
まずはこれで開くことを確認。
ちなみに、名前(上記の例では「child」)を消すと、
リンクを押すたび新しく別のウインドウが開くようになります。
とりあえずこれだけで特に問題なく開きますが、
この後このウインドウを操作することになるので、
オブジェクト変数に入れるようにしておきます。
<jQuery>
var obj; function pushValue(){ obj = window.open("/childwindow.html", "", "width=500, height=500"); }
ウインドウオブジェクト用の変数「obj」を用意しました。
これでウインドウを開ける部分はOK。
値を送る部分
※ローカルでは動かない可能性があるので、
どこかのサーバーで試す必要があるかもしれません
(といってもxampp環境はOKでした)
まず、子ウインドウで値を送るには以下のようにすればOK…らしい。
$(obj.document).find('#testid').val('testvalue');
が、window.openの直後に追加してみても動かない。
とりあえず分割して試してみようと、一旦
リンクも関数も「子ウインドウを開く」と「値を送る」の操作に分けてみると動いた。
しばらく考えてから気付いた。
window.openした後、さらに子ウインドウの読み込みが終わってから送らないとダメか…。
とりあえず、試しに「window.openの3秒後に値を送信」とすれば動いた。
<jQuery>
var obj; function pushValue(){ obj = window.open("/childwindow.html", "child", "width=500, height=500"); setTimeout(function(){ $(obj.document).find('#testid').val('testvalue'); },3000); }
でも読み込みは一瞬で終わるかもしれないし3秒で間に合わないかもしれない。
ので、
「#testidが存在するかどうかを定期的にチェックし、存在すれば送信」
という処理にしてみた。
<jQuery>
var obj; function pushValue(){ obj = window.open("/childwindow.html", "child", "width=500, height=500"); var timer = setInterval(function(){ if ($("#testid", obj.document) != undefined){ $("#testid", obj.document).val('testvalue'); clearInterval(timer); } }, 500); }
必要に応じて整える
ウインドウが既に開かれているかどうかを判定し
開いていなければこれまで書いてきた一連の流れを行い、
開いていればすぐ値を書き換え(つまり後から親ウインドウで操作)…としたかったので、
さっきのかたまりを丸ごとif文で分けた。
タイムアウトとかのロジックを入れた方がいいケースもあるかも。
<jQuery>
var obj; function pushValue(){ if (!obj || obj.closed){ obj = window.open("/childwindow.html", "child", "width=500, height=500"); var timer = setInterval(function(){ if ($("#testid", obj.document) != undefined){ sendValue(); clearInterval(timer); } }, 500); } else{ sendValue(); } } function sendValue(){ $("#testid", obj.document).val("testvalue"); }
参考文献
最終的に使ってない情報も結構ありますが
試行錯誤していて見たものからいくつか。
- JavaScript - 親ウィンドウと子ウィンドウ間でのデータやり取り(28621)|teratail
- jQueryで別ウインドウのイベントをtriggerする ::ハブろぐ
- http://www.openspc2.org/reibun/javascript/sub_window/010/
- ウインドウを閉じる 【JavaScript 動的サンプル】
- 【JavaScript】子ウィンドウ/親ウィンドウのお互いの要素を操作するには!? | Web.fla
- jQueryでsetTimeoutを使ってfunctionの実行を遅らせる方法 | BlackFlag
- 【Javascript】setInterval()で要素が現れるまで待つ
JS/jQueryを使って、動的に生成したURLへ遷移させる
3種類考えたので全部書いとく。
「動的に生成したURL」ってタイトルだけどその生成部分は重要じゃないので普通に固定値で書いてる。
location.hrefを使う方法
<HTML>
<a href="#" onclick="redirect();">ダミーリンク</a>
<jQuery>
function redirect(){ location.href = "http://example.com"; }
簡単。
でもこれを使いたくないパターンがあったので別解を作った。
aタグを書き換える方法
<HTML>
<a href="#" onclick="redirect();" id="linktag">ダミーリンク</a>
<jQuery>
function redirect(){ $("#linktag").attr("href", "http://example.com"); }
この方法ならほかの属性も変更できる。
target="_blank"したりとか。
ただ、aタグの書き換えは行われるけどその後クリックした扱いにならないことがあったので、
その場合は例えば以下のようなのを追加しました。
$("#linktag").attr("onclick", ""); $("#linktag")[0].click();
「.click()」で強制的にクリックさせるけど、
その時にonclickが再発動しないように空にしておく…という感じ。
location.hrefも既存タグ変更もやりたくないケースもあるかもしれないので
さらに別解を作った。
新しいaタグをこっそり作ってそっちを押したことにする方法
<HTML>
<a href="#" onclick="redirect();">ダミーリンク</a>
<jQuery>
function redirect(){ var virtuallink = $("<a></a>", {href: "http://example.com", id: "virtuallink"}); virtuallink.text(" "); $("body")[0].appendChild(virtuallink[0]); $("#virtuallink")[0].click(); }
要するに
<a href="http://example.com" id="virtuallink">
というタグをbodyの一番最後にくっつけて、それにクリックイベントを起こす方法です。
あんまりかっこよくないので、
生成したオブジェクトそのものに対してclickできないかと試したけど動作せず。
(jQueryの最後の行を「 virtuallink[0].click(); 」としても動くのに、
その前の「 $("body")[0].appendChild(virtuallink[0]); 」を削除すると動かない)
zipファイルを生成する
PHPでzipファイルを作る機会があったのでメモ。
関数化したので次からこれ使おう。
<? /* * makeZip * * @param files zipに詰めたいファイル情報の配列 * ただし、ファイル情報は * array('file_path' => "ファイルパス", 'zip_file_name' => "zip内でのファイル名") * という配列で表す * @param zip_path zipのパス * @return 正常ならtrue、失敗したらfalse */ function makeZip($files, $zip_path, $mode = 0755) { // 既にファイルが存在する場合は削除 if (file_exists($zip_path)) { @unlink($zip_path); } // ZipArchiveはPHP5.2以降なら標準で使用可能 $zip = new ZipArchive(); // オープンできなければエラー終了 if ($res = $zip->open($zip_path, ZipArchive::CREATE) !== true) { return false; } // 渡されたファイルを順番にzipに詰める foreach ($files as $file) { // 詰めるのに失敗したらエラー終了 if ($zip->addFile($file['file_path'], $file['zip_file_name']) == false) { $zip->close(); @unlink($zip_path); return false; } } // クローズしてパーミッション変更 $zip->close(); chmod($zip_path, $mode); // 正常終了 return true; }
呼び出し部分の例
<? $file_list = array(); $file_list[] = array( 'zip_file_name' => "test01.jpg", 'file_path' => "./files/test01.jpg", ); $file_list[] = array( 'zip_file_name' => "test02.jpg", 'file_path' => "./files/test02.jpg", ); $this->makeZip($file_list, "./zip/test.zip");