Button Control: Simple Calendar Menu Button
このExampleの初期画面は以下。見ての通りのカレンダーピッカー(Calendar Picker)である(YUIのExampleへのリンクはこちら)。
カレンダーアイコンのついているボタンをクリックすると、以下のようにカレンダー(メニュー)が表示される。このとき、「今日」にフォーカスがあたるようになっている(太めの点線で囲まれたところ)。
ここで、カレンダーから日付をピックアップすると、それがテキストフィールドに反映される。下の図は、その状態で再度、カレンダーボタンをクリックした状態。選択した日付が青く表示される。
こんなシンプルなカレンダーピッカーだが、Javascriptは意外に面倒くさい。
YUIのExampleのコードをmodule patternに書き直して、冗長と思われる部分を削除したhtmlを以下に示す。ボタンにカレンダーオブジェクトをしのばせる方法は、「Button Control: Menu Buttons」の2で示した方法である。
以下のscriptでは、onButtonClick()は初回クリック時の1回だけ呼ばれるようにする(関数末にボタンのclickイベントのハンドラをunsubscribeしている)。そうしないと、ボタンクリック時にカレンダーインスタンスが再作成されてしまい、選択した日付を忘れてしまう(日付の選択後に、再度ボタンを押した際に、3番目の画面が表示されず、1番目の画面がでてしまう)。
<HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <TITLE>Ajax_Sampling</TITLE> <style type="text/css"> body { margin:0; padding:0; } </style> <link rel="stylesheet" type="text/css" href="scripts/yui/fonts/fonts-min.css" /> <link rel="stylesheet" type="text/css" href="scripts/yui/button/assets/skins/sam/button.css" /> <link rel="stylesheet" type="text/css" href="scripts/yui/calendar/assets/skins/sam/calendar.css" /> <script type="text/javascript" src="scripts/yui/yahoo-dom-event/yahoo-dom-event.js"></script> <script type="text/javascript" src="scripts/yui/container/container_core-min.js"></script> <script type="text/javascript" src="scripts/yui/calendar/calendar-min.js"></script> <script type="text/javascript" src="scripts/yui/element/element-beta-min.js"></script> <script type="text/javascript" src="scripts/yui/button/button-min.js"></script> <style type="text/css" id="defaultstyle"> #container { margin: 2px; padding: 3px; width: 500px; height: auto; border:1px dashed #999999; } /* Set the "zoom" property to "normal" since it is set to "1" by the ".example-container .bd" rule in yui.css and this causes a Menu instance's width to expand to 100% of the browser viewport. */ div.yuimenu .bd { zoom: normal; } /* Restore default padding of 10px for the calendar containtainer that is overridden by the ".example-container .bd .bd" rule in yui.css. */ #calendarcontainer { padding:10px; } #calendarmenu { position: absolute; } #calendarpicker button { background: url(http://developer.yahoo.com/yui/examples/button/assets/calendar_icon.gif) center center no-repeat; text-align: left; text-indent: -10em; overflow: hidden; *margin-left: 10em; /* For IE */ *padding: 0 3em; /* For IE */ white-space: nowrap; } #month-field, #day-field { width: 2em; } #year-field { width: 3em; } #datefields { border: solid 1px #666; padding: .5em; } #calendarpicker { vertical-align: baseline; } </style> <script type="text/javascript"> // //モジュールパターンで実装する。 // YAHOO.namespace("EGP"); YAHOO.EGP.CalendarButton = function() { var Event = YAHOO.util.Event; var Dom = YAHOO.util.Dom; var oCalendarMenu; var oButton; // カレンダー表示ボタンクリック時のハンドラー var onButtonClick = function () { // Create a Calendar instance and render it into the body // element of the Overlay. // OVerlayインスタンスのボディー部にid=buttoncalendarでカレンダーインスタンス // を作る。 // (※)カレンダーインスタンスは1回しか作成されない。 var oCalendar = new YAHOO.widget.Calendar("buttoncalendar", oCalendarMenu.body.id); // Subscribe to the Calendar instance's "select" event to // update the month, day, year form fields when the user // selects a date. // カレンダーのselectイベントに、markupへの日付の編集と、カレンダーの非表示を // sbscribeする。 oCalendar.selectEvent.subscribe(function (p_sType, p_aArgs) { var aDate; if (p_aArgs) { aDate = p_aArgs[0][0]; Dom.get("month-field").value = aDate[1]; Dom.get("day-field").value = aDate[2]; Dom.get("year-field").value = aDate[0]; } oCalendarMenu.hide(); }); // Set focus to either the current day, or first day of the month in // the Calendar when it is made visible or the month changes // Menuのshowイベントに対して、focusDayを仕掛ける。 // 本日をフォーカスさせ、ページを送ったら先頭日にフォーカスを当てる(点線の枠)。 oCalendar.renderEvent.subscribe(focusDay, oCalendar, true); // カレンダーの表示 oCalendar.render(); // Unsubscribe from the "click" event so that this code is // only executed once // Buttonのクリックは1回だけなので、unsubscribeする。 // この後のボタンクリックは、ボタンインスタンスを再表示してMenu処理をするだけ。 // (これは、oButton生成時に設定される) this.unsubscribe("click", onButtonClick); }; // 日にちにfocusを当てるための関数。 var focusDay = function () { // Calelndarインスタンスを生成したDOM(id="buttoncalendar")から // Table Bodyをとってくる。http://www.howtocreate.co.uk/tutorials/javascript/domtables var oCalendarTBody = Dom.get("buttoncalendar").tBodies[0], // カレンダーの全ての日を取ってくる(aタグで書かれている)。 aElements = oCalendarTBody.getElementsByTagName("a"), oAnchor; if (aElements.length > 0) { // Dom.batch(Array,fn)は、Array要素に対して、繰り返しfnを適用する。 Dom.batch(aElements, function (element) { // elementの親ノードが"today"というclassをもてば、アンカー // をそのエレメントに貼る。 // この場合は、td要素。 if (Dom.hasClass(element.parentNode, "today")) { oAnchor = element; } } ); // アンカーが貼られなかったら、先頭日に貼る。 if (!oAnchor) { oAnchor = aElements[0]; } // Focus the anchor element using a timer since Calendar will try // to set focus to its next button by default // 0msec後にoAnchorにたいして、functionを実行。 YAHOO.lang.later(0, oAnchor, function () { try { oAnchor.focus(); } catch(e) {} } ); } }; return{ init: function() { // Create an Overlay instance to house the Calendar instance // Calendarインスタンスを入れるOverlayインスタンスの作成。 // id=calendarmenuとする。 oCalendarMenu = new YAHOO.widget.Overlay("calendarmenu", { visible: false }); // Create a Button instance of type "menu" // ボタンインスタンスをtype=menuで作成する。 oButton = new YAHOO.widget.Button({ type: "menu", id: "calendarpicker", label: "Choose A Date", menu: oCalendarMenu, container: "datefields" }); // ボタンのappendToイベントに対して、Calendarインスタンスを入れるための // 空のbodyをセットする。 // このbodyのidをcalendarcontainerとする。 oButton.on("appendTo", function () { // Create an empty body element for the Overlay instance // in order // to reserve space to render the Calendar instance into. oCalendarMenu.setBody(" "); oCalendarMenu.body.id = "calendarcontainer"; }); // Add a "click" event listener that will render the Overlay, and // instantiate the Calendar the first time the Button instance is // clicked. // ボタンのクリックイベントのハンドラとして、onButtonClickを設定する。 oButton.on("click", onButtonClick); // Pressing the Esc key will hide the Calendar Menu and send focus back to // its parent Button // oCalendarMenu上でkeyが押され、それがesc keyの場合、フォーカスを // ボタンに返す。 Event.on(oCalendarMenu.element, "keydown", function (p_oEvent) { if (Event.getCharCode(p_oEvent) === 27) { oCalendarMenu.hide(); oButton.focus(); } }, null, oButton); } }; }(); //DOMが完全にloadされたら、サンプルを初期化する。 YAHOO.util.Event.onDOMReady( //DomReadyイベントで発火するハンドラ YAHOO.EGP.CalendarButton.init, //ハンドラに渡すオブジェクト(関数) YAHOO.EGP.CalendarButton, //ハンドラは、上記のオブジェクトのスコープをもつ。 true ); </script> </HEAD> <BODY class="yui-skin-sam"> <div id="container"> <p> Calendar Buttonの作り方のサンプルです。 </p> <form id="button-example-form" name="button-example-form" method="post"> <fieldset id="datefields"> <legend>Date</legend> <label for="year-field">Year: </label> <input id="year-field" type="text" name="year"> <label for="month-field">Month: </label> <input id="month-field" type="text" name="month"> <label for="day-field">Day:</label> <input id="day-field" type="text" name="day"> </fieldset> </form> </div> </BODY> </HTML>