SQLの窓

2018年02月01日


jQuery UI の Autocomplete Combobox をさらにカスタマイズして、『Form の送信で使用する入力可能なコンボボックス』にする



jQuery UI のデモでは、入力した内容がリストに無ければエラーになりますが、そのチェック部分を省いて、さらに内部で作成される INPUT 要素に id 属性と name 属性をセットするインターフェイスを追加しました。

FORM で送信すると、リストに無い内容(例:VB)だと ?basecombo=&combo_input=VB となり、存在するものだと basecombo と combo_input は同じになります
id と name には、data メソッドで引き渡した内容(key:input_name)をセットします ※ 現時点(2018/02/01)で jQuery UI は、1.12.1 である必要があります
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<link id="link" rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>

<style>
.custom-combobox {
	position: relative;
	display: inline-block;
}
.custom-combobox-toggle {
	position: absolute;
	top: 0;
	bottom: 0;
	margin-left: -1px;
	padding: 0;
}
.custom-combobox-input {
	margin: 0;
	padding: 5px 10px;
}
</style>

<script>
$( function() {
	$.widget( "custom.combobox", {
		_create: function() {
			this.wrapper = $( "<span>" )
				.addClass( "custom-combobox" )
				.insertAfter( this.element );

			this.element.hide();
			this._createAutocomplete();
			this._createShowAllButton();
		},

		_createAutocomplete: function() {
			var selected = this.element.children( ":selected" ),
				value = selected.val() ? selected.text() : "";

			var name = $(this.element).data("input_name");
			this.input = $( "<input id=\""+name+"\" name=\""+name+"\">" )
				.appendTo( this.wrapper )
				.val( value )
				.attr( "title", "" )
				.addClass( "custom-combobox-input ui-widget ui-widget-content ui-state-default ui-corner-left" )
				.autocomplete({
					delay: 0,
					minLength: 0,
					source: $.proxy( this, "_source" )
				})
				.tooltip({
					classes: {
						"ui-tooltip": "ui-state-highlight"
					}
				});

			this._on( this.input, {
				autocompleteselect: function( event, ui ) {
					ui.item.option.selected = true;
					this._trigger( "select", event, {
						item: ui.item.option
					});
				},

				autocompletechange: "_removeIfInvalid"
			});
		},

		_createShowAllButton: function() {
			var input = this.input,
				wasOpen = false;

			$( "<a>" )
				.attr( "tabIndex", -1 )
				.attr( "title", "リストを開く" )
				.tooltip()
				.appendTo( this.wrapper )
				.button({
					icons: {
						primary: "ui-icon-triangle-1-s"
					},
					text: false
				})
				.removeClass( "ui-corner-all" )
				.addClass( "custom-combobox-toggle ui-corner-right" )
				.on( "mousedown", function() {
					wasOpen = input.autocomplete( "widget" ).is( ":visible" );
				})
				.on( "click", function() {
					input.trigger( "focus" );

					// Close if already visible
					if ( wasOpen ) {
						return;
					}

					// Pass empty string as value to search for, displaying all results
					input.autocomplete( "search", "" );
				});
		},

		_source: function( request, response ) {
			var matcher = new RegExp( $.ui.autocomplete.escapeRegex(request.term), "i" );
			response( this.element.children( "option" ).map(function() {
				var text = $( this ).text();
				if ( this.value && ( !request.term || matcher.test(text) ) )
					return {
						label: text,
						value: text,
						option: this
					};
			}) );
		},

		_removeIfInvalid: function( event, ui ) {

			// Selected an item, nothing to do
			if ( ui.item ) {
				return;
			}

			// Search for a match (case-insensitive)
			var value = this.input.val(),
				valueLowerCase = value.toLowerCase(),
				valid = false;
			this.element.children( "option" ).each(function() {
				if ( $( this ).text().toLowerCase() === valueLowerCase ) {
					this.selected = valid = true;
					return false;
				}
			});

			// Found a match, nothing to do
			if ( valid ) {
				return;
			}

			// Remove invalid value

		},

		_destroy: function() {
			this.wrapper.remove();
			this.element.show();
		}
	});

	$( "#combobox" )
		.data("input_name", "combo_input")
		.combobox();

} );
</script>

<form>
<div class="ui-widget">
	<select id="combobox" name="basecombo">
		<option value=""></option>
		<option value="C">C</option>
		<option value="C++">C++</option>
		<option value="Java">Java</option>
		<option value="JavaScript">JavaScript</option>
		<option value="Perl">Perl</option>
		<option value="PHP">PHP</option>
		<option value="Python">Python</option>
		<option value="Ruby">Ruby</option>
	</select>
</div>
<input type="submit">
</form>

関連する記事

使いどころが難しいですが、入力をコンボボックス化する jQuery プラグインの実装が不便だったので、modify しました。


posted by lightbox at 2018-02-01 14:06 | jQuery UI | このブログの読者になる | 更新情報をチェックする

2015年08月11日


Google Picasa サービスの URL を使用して、jQuery のスライダーでリアルタイムにサイズの違った画像を表示する

これは、Picasa サービスの URL 部分を変更すると、Picasa 側でそのサイズの画像を作成してくれる事の確認プログラムです。最近、Picasa の URL の仕様が少し変わり、"s長辺のサイズ" の後ろに "-Ic42" が付くようになりましたが、意味は謎です。

スライダーを動かすと、画像サイズが変化しますが、これはそのサイズの画像を表示しています。ブラウザは縮小も拡大もしていません。

この仕様は正式な文書は無いかもしれませんが、Picasa のサービス内でユーザが利用する文字列なので、Picasa サービスが余程の変更されない限り利用が継続できるはずです。

"s長辺のサイズ" 以外にも、"w横幅" と "h高さ" も使えますが、これは以前 Google+ の画像に使用されていたもので、現在も使えますが、この二つはいつ使えなくなってもおかしくない仕様です。(そもそも、Picasa も Google+ の画像も元は同じようです)

活用方法

この仕様と、正規表現の置換を用いれば、一つの URL から複数サイズの画像の URL を動的に作成できるので、簡単な使い方の例としては、表示画像(サムネイル)と目的画像というコンテンツを一度での用意です。




<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<link id="link" rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/themes/base/jquery-ui.css">
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script>
var url = "https://lh3.googleusercontent.com/-CuiK3fGTUAw/Vak2SZiXCXI/AAAAAAAAbJM/RpiEqBN0hmw/s100-Ic42/56ce94dfc3fc6f3df8240e7b6cbbadc6.png"

$(function(){

	// 初期画像
	$("#picasa_img").prop("src", url);

	// スライダー
	$("#slider1").slider({
		range: "min",	// 左側を着色する
		step: 50,	// 50 づつ変更
		min: 50,	// 最小50
		max: 600,	// 最大600
		value: 100,	// 初期値
		// スライダーが変化した時のイベント
		slide: function (event, ui) {
			// src に無名関数の戻り値を設定する
			$("#picasa_img").prop("src", function(){
				// s100 部分の 100 を変更する為の正規表現を使った replace
				return url.replace(/\/s\d+\-Ic42\//, "/s"+ui.value+"-Ic42/");
			});
		}
	})
	.css("width", "400px")
	
});

</script>
<div id="slider1"></div>
<br><br>
<img id="picasa_img">



posted by lightbox at 2015-08-11 22:51 | jQuery UI | このブログの読者になる | 更新情報をチェックする

2015年07月16日


Google がホスティングしている jQuery UI の CSS のテーマのチェックをする jQuery UI ボタン

オンライン実行

ボタンクリックすると、テーマを差し替えてそれぞれのテーマでのボタン表示になります。

Google では、スクリプトのほうは全てのバージョンをホスティングしていますが、CSS のほうは 1.10.1 以前でないと base テーマが存在しません( 他のテーマはあるようなのですが・・・ )

▼ こんな感じです


ここでは、1.10.1 のテーマを使って表示しています。
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<link id="link" rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/themes/base/jquery-ui.css">
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script type="text/javascript">

$(function(){

	var css_type = [
		"base","ui-lightness","ui-darkness","smoothness","start","redmond",
		"sunny","overcast","le-frog","flick","pepper-grinder","eggplant",
		"dark-hive","cupertino","south-street","blitzer","humanity","hot-sneaks",
		"excite-bike","vader","dot-luv","mint-choc","black-tie","trontastic","swanky-purse"
	]
	var cnt = 0;
	// ボタン
	$("#button_control")
		.button()
		.val("base")
		.click(function(){
			if ( cnt >= css_type.length-1 ) {
				cnt = -1;
			}
			cnt++;
			css_target = "//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/themes/";
			css_target += css_type[cnt];
			css_target += "/jquery-ui.css";
			$("#link").prop("href", css_target );
			$(this).val(css_type[cnt]);
		});

});

</script>

<input id="button_control">



posted by lightbox at 2015-07-16 17:17 | jQuery UI | このブログの読者になる | 更新情報をチェックする

jQuery UI の Spinner のボタン部分だけを利用して、マウスでクリックしたままでイベントが継続する増減ボタンを作成する

オンライン実行

元の INPUT 要素は使用しないので、style='display:none' を指定しておきます。個別に jQuery の css メソッドで調整を行います。
	.css({ 
		width: "0px",
		"font-size": "0px",
		height: "30px"
	 })

▼ 以下は実行サンプルですが、スタイルとして『base』を使用しているのでボタン部分がグレーになっています。
x / y



x y
x y
横向きになっているのは、jQuery からの CSS 設定で回転させています
	// コントロールを横にしたい場合
	// ※ コントロールのサイズや左右の余白を調整します
	$(".rotate_wrap").css({
		display: "inline-block",
		transform: "rotate(90deg) scale(1.5)",
		"margin":"0 15px 0 18px",
	});

以下の部分は、ボタンとは関係無く、テキストフィールドに対して結果を表示する際に、parseInt($(this).val()) が失敗する場合は 0 をセットし、そこから値を減らしています。
$(this).val( (parseInt($(this).val())||0)-1 );
▼が全体のソースコードですが、ボタンを回転させたり、サイズや配置を調整する為にはラッパーが必要なのが解ります。
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/themes/base/jquery-ui.css">
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/jquery-ui.min.js"></script>
<script>

$(function(){

	// コントロールを横にしたい場合
	// ※ コントロールのサイズや左右の余白を調整します
	$(".rotate_wrap").css({
		display: "inline-block",
		transform: "rotate(90deg) scale(1.5)",
		"margin":"0 15px 0 18px",
	});
	// 通常は縦です
	$(".normal_wrap").css({
		display: "inline-block",
		transform: "scale(1.5)",
		"margin":"0 15px 0 7px",
	});

	$( "#spinner_x" ).spinner({
		spin: function( event, ui ) {
			var val = $(this).val()-ui.value;
			// ▲ をクリックするか押し続けると処理されます
			if ( val < 0 ) {
				$( ".x" ).each(function(){
					// 数字以外が設定されているフィールドは 0 にリセットする
					$(this).val( (parseInt($(this).val())||0)+1 );
				});
			}
			// ▼ をクリックするか押し続けると処理されます
			else {
				$( ".x" ).each(function(){
					// 数字以外が設定されているフィールドは 0 にリセットする
					$(this).val( (parseInt($(this).val())||0)-1 );
				});
			}
		}
	})
	// 入力部分を使わないので、最小のサイズにして、高さをセット
	.css({ 
		width: "0px",
		"font-size": "0px",
		height: "30px"
	 })
	// いったん表示して
	.show()
	// spinner のラッパーを調整します
	.parent().css({width:"16px",border:"1px solid #808080"});

	$( "#spinner_y" ).spinner({
		spin: function( event, ui ) {
			var val = $(this).val()-ui.value;
			// ▲ をクリックするか押し続けると処理されます
			if ( val < 0 ) {
				$( ".y" ).each(function(){
					// 数字以外が設定されているフィールドは 0 にリセットする
					$(this).val( (parseInt($(this).val())||0)+1 );
				});

			}
			// ▼ をクリックするか押し続けると処理されます
			else {
				$( ".y" ).each(function(){
					// 数字以外が設定されているフィールドは 0 にリセットする
					$(this).val( (parseInt($(this).val())||0)-1 );
				});
			}
		}
	})
	// 入力部分を使わないので、最小のサイズにして、高さをセット
	.css({ 
		width: "0px",
		"font-size": "0px",
		height: "30px"
	 })
	// いったん表示して
	.show()
	// spinner のラッパーを調整します
	.parent().css({width:"16px",border:"1px solid #808080"});



});


</script>

<div id="spinner_box">
x
<input id="xnum" type="text" size="3" class="x">
<span class="rotate_wrap"><input id="spinner_x" style='display:none;'></span>
/ 
y
<input id="ynum" type="text" size="3" class="y">
<span class="normal_wrap"><input id="spinner_y" style='display:none'></span>

<br><br><br><br>
x <input type="text" size="3" class="x">
y <input type="text" size="3" class="y">
<br>
x <input type="text" size="3" class="x">
y <input type="text" size="3" class="y">

</div>




タグ:jquery jQuery UI
posted by lightbox at 2015-07-16 14:20 | jQuery UI | このブログの読者になる | 更新情報をチェックする

2014年11月24日


jQuery UI の Accordion Widget(アコーディオン)で、タイトルとコンテンツの指定方法

タイトルとコンテンツ

指定方法は3種類ありますが、基本はアコーディオンを指定したブロックの子要素の偶数番号がタイトルになり、その直後の要素がコンテンツになります。
Content panels must be the sibling immediately after their associated headers.

コンテンツパネルは、ヘッダの直後の兄弟要素でなければなりません。
但し例外があって、子要素の中に LI 要素が並ぶ場合( 通常親要素は UL )は、LI 内の最初の子要素がタイトルになり、それに続く要素がコンテンツになります。( セレクタ : > li > :first-child ) もう一つ、header オプションでタイトルとする要素を指定する事ができます。これは、無条件に偶数をタイトルにすると、非表示要素が対象になってしまいます。そのような事を避ける為に header オプションがあります コンテンツはブロック要素で heightStyle というオプション( 規定値は auto )があり、fill、auto、content が指定できます。いずれも、正しく表示する為にコンテンツの高さが必要になってくるので、コンテンツ部分はブロック要素で指定します。 注意するのは、fill の場合で、アコーディオンを指定した親要素の高さいっぱいに広がるようになります。レイアウトが変化しては困る場合に親要素の指定は有用ですが、その為に親要素を正しくレイアウトする必要があります。 auto は、複数コンテンツがある場合、そのコンテンツの中で一番高さが大きいものにあわせられます。順に内容を読んで行くようなコンテンツに適しています。 content は、コンテンツ毎の高さで表示されるので、オプション表示等に向いています
  • 最初の要素が li の場合、li の child の最初の要素がタイトルとなります
    コンテンツは高さのコントロールがあるので、
    ブロック要素が望ましいようです
  • heightStyle が fill の場合は
    親要素の高さいっぱいに広がります
  • ヘッダは通常は偶数で、コンテンツは奇数です
    header オプションで要素を指定も可能です
  • ▲ いっぱいいっぱいまで使用しているので、親要素の bottom と重なっています
    active オプションで最初に開くコンテンツを指定できます
    この内容を localStorage に保存しておけば、次回に開く処理を実装できます
    heightStyle が auto の場合は
    コンテンツの中で
    一番大きな高さにあわせます
    このアコーディオンのヘッダは
    偶数要素が使われています
    ▼ ヘッダ部分の輪郭のスタイルが違うのは、このブログ内の H3 に対するスタイルです

    ヘッダを強制的に指定しています

    コンテンツはその次のみで、複数が続くと、2つ目以降は正しく表示されません

    heightStyle が content の場合は

    コンテンツのの
    高さにあわせます

    UI の基本スタイルを変更したい場合は

    新しいクラスを定義し、追加して上書きします
    
    
    
    ▼ ソースコードサンプル
    
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
    <link rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/themes/base/jquery-ui.css">
    <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/jquery-ui.min.js"></script>
    <script>
    $(function(){
    	$( ".wrap" ).css( { height: "250px", border: "solid 2px #000", "margin-bottom": "20px" } );
    	$( "#accordion1" ).accordion({
    		heightStyle: "fill"
    	});
    
    	$( "#accordion2" ).accordion({
    		heightStyle: "auto",
    		active: 1
    	});
    
    	$( "#accordion3" ).accordion({
    		heightStyle: "content",
    		header: "h3"
    	})
    	.addClass( "accordion3_style" );
    
    });
    </script>
    <style type="text/css">
    .accordion3_style {
    	font-size: 14px;
    	font-family: "メイリオ","MS ゴシック";
    }
    </style>
    <div class="wrap">
    <div id="accordion1">
    	<li><span>最初の要素が li の場合、li の child の最初の要素がタイトルとなります</span><div>コンテンツは高さのコントロールがあるので、<br>ブロック要素が望ましいようです</div></li>
    	<li><span>heightStyle が fill の場合は</span><div>親要素の高さいっぱいに広がります</div></li>
    	<li><span>ヘッダは通常は偶数で、コンテンツは奇数です</span><div>header オプションで要素を指定も可能です</div></li>
    </div>
    </div>
    
    <div class="wrap">
    <div id="accordion2">
    	<span>active オプションで最初に開くコンテンツを指定できます</span>
    	<div>この内容を localStorage に保存しておけば、次回に開く処理を実装できます</div>
    
    	<b>heightStyle が auto の場合は</b>
    	<div>コンテンツの中で<br>一番大きな高さにあわせます</div>
    
    	<div>このアコーディオンのヘッダは</div>
    	<div>偶数要素が使われています</div>
    </div>
    </div>
    
    <div class="wrap">
    <div id="accordion3">
    	<h3>ヘッダを強制的に指定しています</h3>
    	<div>コンテンツはその次のみで、複数が続くと、2つ目以降は正しく表示されません</div>
    
    	<h3>heightStyle が content の場合は</h3>
    	<div>コンテンツのの<br>高さにあわせます</div>
    
    	<h3>UI の基本スタイルを変更したい場合は</h3>
    	<div>新しいクラスを定義し、追加して上書きします</div>
    </div>
    </div>
    
    
    
    
    
    posted by lightbox at 2014-11-24 12:51 | jQuery UI | このブログの読者になる | 更新情報をチェックする

    2014年10月21日


    jQuery : table の行をクリックして、jQuery UI のダイアログを表示し、行データをダイアログ内で変更して table の列にデータを戻す

    
    
    だいたい想定される、table データを jQuery で行単位で処理する方法です。ダイアログを表示する位置は、デバイスによって調整する必要があると思いますが、殆どの目的はこんな感じで対処できると思います。
    
    table の行をクリック( タップ ) すると、モーダルダイアログ( 実際は DIV )を開いて、行のデータをダイアログ内に転送して処理します。
    
    数値入力は、モバイルを想定して type="tel" にして、iPhone4S で動作確認しました。
    
    001 りんご 500
    002 いちご 300
    
    ▼ ソースコード
    
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
    <link id="link" rel="stylesheet" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/base/jquery-ui.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
    
    <style type="text/css">
    #data {
    	border-collapse: collapse;
    	border: solid #000000 1px;
    }
    #data tr {
    	cursor: pointer;
    }
    #data td {
    	padding: 5px;
    	border: solid #000000 1px;
    }
    </style>
    <script type="text/javascript">
    $(function(){
    	$("#data tr").each(function(index){
    		$(this).click(function(){
    			$( "#code" ).text( $(this).children( "td" ).eq(0).text() );
    			$( "#namae" ).text( $(this).children( "td" ).eq(1).text() );
    			$( "#kingaku" ).val( $(this).children( "td" ).eq(2).text() );
    			var jq = $(this);
    			$( "#dialog-message" ).dialog({
    				modal: true,
    				title: "ダイアログのタイトルです",
    				close: function() {
    					console.log("x ボタンがクリックされました");
    				},
    				buttons: [
    					{ 
    						text: "確認",
    						click: function() {
    							jq.children( "td" ).eq(2).text( $( "#kingaku" ).val() );
    							$( this ).dialog( "close" );
    							console.log("確認 ボタンがクリックされました");
    						}
    					},
    					{
    						text: "キャンセル",
    						click: function() {
    							$( this ).dialog( "close" );
    							console.log("キャンセル ボタンがクリックされました");
    						}
    					}
    				]
    			});
    		});
    	});
    });
    </script>
    <table id="data">
    <tr>
    	<td class="code">001</td>
    	<td class="name">りんご</td>
    	<td class="value">500</td>
    </tr>
    <tr>
    	<td class="code">002</td>
    	<td class="name">いちご</td>
    	<td class="value">300</td>
    </tr>
    </table>
    
    <div id="dialog-message" title="" style='display:none;'>
    コード <span id="code"></span><br>
    名称 <span id="namae"></span><br>
    金額 <input id="kingaku" type="tel" size="6">
    </div>
    
    jq は、クリック( またはタップ )された jQuery の行(tr) オブジェクトです。この保存しておいた(25行)オブジェクトを使用して、入力したデータを行に戻しています(36行)。
    
    
    
    
    posted by lightbox at 2014-10-21 21:05 | jQuery UI | このブログの読者になる | 更新情報をチェックする
    Seesaa の各ページの表示について
    Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。
    
    Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。
    
    また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。
    
    ※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです
    
    対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。
    
    
    ※ エキスパートモードで表示しています
    
    アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります
    
    
    <% if:page_name eq 'archive' -%>
    アーカイブページでのみ表示される内容
    <% /if %>
    
    <% if:page_name eq 'category' -%>
    カテゴリページでのみ表示される内容
    <% /if %>
    
    <% if:page_name eq 'tag' -%>
    タグページでのみ表示される内容
    <% /if %>
    
    この記述は、以下の場所で使用します
    container 終わり

    フリーフォントで簡単ロゴ作成
    フリーフォントでボタン素材作成
    フリーフォントで吹き出し画像作成
    フリーフォントではんこ画像作成
    ほぼ自由に利用できるフリーフォント
    フリーフォントの書体見本とサンプル
    画像を大きく見る為のウインドウを開くボタンの作成

    CSS ドロップシャドウの参考デモ
    BUTTONS (CSS でボタン)
    イラストAC
    ぱくたそ
    写真素材 足成
    フリーフォント一覧
    utf8 文字ツール
    右サイド 終わり
    base 終わり