SitePoint Sponsor

User Tag List

Results 1 to 4 of 4
  1. #1
    Visible Ninja bronze trophy
    JeffWalden's Avatar
    Join Date
    Sep 2002
    Location
    Los Angeles
    Posts
    1,709
    Mentioned
    5 Post(s)
    Tagged
    0 Thread(s)

    Call Javascript Function With Other Element

    I'm using a simple datepicker script found here: simple jQuery date-picker plugin

    The actual code is here: http://teddevito.com/demos/cal.js

    When the input text element receives focus (through onclick?) the calendar HTML is displayed. I'd like to call disable that and only display the HTML when the user clicks on a calendar icon next to the text box. Additionally, I'd like to close the calendar HTML when the text box loses focus, which does not currently happen.

    Javascript is not my strong point and I'm a little lost looking at this code. Any help or even a pointer in the right direction would be most appreciated.
    TAKE A WALK OUTSIDE YOUR MIND.

  2. #2
    SitePoint Addict Zarin Denatrose's Avatar
    Join Date
    Jan 2009
    Location
    Surrey BC, Canada
    Posts
    309
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hey there. I've modified the code to act in the following manner:
    1. Calendars do not open when clicking on inputs
    2. Calendars open when clicking on their sibling .calpic elements.

    The way I have this set up, the input and .calpic should be in a containing element of some kind.

    For instance, here is my test element:

    Code:
        <div>
          <input type="text" />
          <div class='calpic'>C</div>
        </div>
    .calpic may be an img, so long as it has the calpic class. You can change that class in the javascript below.

    You cannot have these calendars close when the input loses focus, because clicking on the calendar itself would cause that to happen. you may be able to have the datepickers close when clicking anything other than the datepicker, but it would require some heavier modification. You can see the areas I have changed denoted with comments beginning with this: //<--

    I hope this at least gets you pointed in the right direction!

    Code:
    /**
    	the script only works on "input [type=text]"
    
    **/
    
    // don't declare anything out here in the global namespace
    
    (function($) { // create private scope (inside you can use $ instead of jQuery)
    
        // functions and vars declared here are effectively 'singletons'.  there will be only a single
        // instance of them.  so this is a good place to declare any immutable items or stateless
        // functions.  for example:
    
    	var today = new Date(); // used in defaults
        var months = 'January,February,March,April,May,June,July,August,September,October,November,December'.split(',');
    	var monthlengths = '31,28,31,30,31,30,31,31,30,31,30,31'.split(',');
      	var dateRegEx = /^\d{1,2}\/\d{1,2}\/\d{2}|\d{4}$/;
    	var yearRegEx = /^\d{4,4}$/;
    
        // next, declare the plugin function
        $.fn.simpleDatepicker = function(options) {
    
            // functions and vars declared here are created each time your plugn function is invoked
    
            // you could probably refactor your 'build', 'load_month', etc, functions to be passed
            // the DOM element from below
    
    		var opts = jQuery.extend({}, jQuery.fn.simpleDatepicker.defaults, options);
    		
    		// replaces a date string with a date object in opts.startdate and opts.enddate, if one exists
    		// populates two new properties with a ready-to-use year: opts.startyear and opts.endyear
    		
    		setupYearRange();
    		/** extracts and setup a valid year range from the opts object **/
    		function setupYearRange () {
    			
    			var startyear, endyear;  
    			if (opts.startdate.constructor == Date) {
    				startyear = opts.startdate.getFullYear();
    			} else if (opts.startdate) {
    				if (yearRegEx.test(opts.startdate)) {
    				startyear = opts.startdate;
    				} else if (dateRegEx.test(opts.startdate)) {
    					opts.startdate = new Date(opts.startdate);
    					startyear = opts.startdate.getFullYear();
    				} else {
    				startyear = today.getFullYear();
    				}
    			} else {
    				startyear = today.getFullYear();
    			}
    			opts.startyear = startyear;
    			
    			if (opts.enddate.constructor == Date) {
    				endyear = opts.enddate.getFullYear();
    			} else if (opts.enddate) {
    				if (yearRegEx.test(opts.enddate)) {
    					endyear = opts.enddate;
    				} else if (dateRegEx.test(opts.enddate)) {
    					opts.enddate = new Date(opts.enddate);
    					endyear = opts.enddate.getFullYear();
    				} else {
    					endyear = today.getFullYear();
    				}
    			} else {
    				endyear = today.getFullYear();
    			}
    			opts.endyear = endyear;	
    		}
    		
    		/** HTML factory for the actual datepicker table element **/
    		// has to read the year range so it can setup the correct years in our HTML <select>
    		function newDatepickerHTML () {
    			
    			var years = [];
    			
    			// process year range into an array
    			for (var i = 0; i <= opts.endyear - opts.startyear; i ++) years[i] = opts.startyear + i;
    	
    			// build the table structure
    			var table = jQuery('<table class="datepicker" cellpadding="0" cellspacing="0"></table>');
    			table.append('<thead></thead>');
    			table.append('<tfoot></tfoot>');
    			table.append('<tbody></tbody>');
    			
    				// month select field
    				var monthselect = '<select name="month">';
    				for (var i in months) monthselect += '<option value="'+i+'">'+months[i]+'</option>';
    				monthselect += '</select>';
    			
    				// year select field
    				var yearselect = '<select name="year">';
    				for (var i in years) yearselect += '<option>'+years[i]+'</option>';
    				yearselect += '</select>';
    			
    			jQuery("thead",table).append('<tr class="controls"><th colspan="7"><span class="prevMonth">&laquo;</span>&nbsp;'+monthselect+yearselect+'&nbsp;<span class="nextMonth">&raquo;</span></th></tr>');
    			jQuery("thead",table).append('<tr class="days"><th>S</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th></tr>');
    			jQuery("tfoot",table).append('<tr><td colspan="2"><span class="today">today</span></td><td colspan="3">&nbsp;</td><td colspan="2"><span class="close">close</span></td></tr>');
    			for (var i = 0; i < 6; i++) jQuery("tbody",table).append('<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr>');	
    			return table;
    		}
    		
    		/** get the real position of the input (well, anything really) **/
    		//http://www.quirksmode.org/js/findpos.html
    		function findPosition (obj) {
    			var curleft = curtop = 0;
    			if (obj.offsetParent) {
    				do { 
    					curleft += obj.offsetLeft;
    					curtop += obj.offsetTop;
    				} while (obj = obj.offsetParent);
    				return [curleft,curtop];
    			} else {
    				return false;
    			}
    		}
    		
    		/** load the initial date and handle all date-navigation **/
    		// initial calendar load (e is null)
    		// prevMonth & nextMonth buttons
    		// onchange for the select fields
    		function loadMonth (e, el, datepicker, chosendate) {
    			
    			// reference our years for the nextMonth and prevMonth buttons
    			var mo = jQuery("select[name=month]", datepicker).get(0).selectedIndex;
    			var yr = jQuery("select[name=year]", datepicker).get(0).selectedIndex;
    			var yrs = jQuery("select[name=year] option", datepicker).get().length;
    			
    			// first try to process buttons that may change the month we're on
    			if (e && jQuery(e.target).hasClass('prevMonth')) {				
    				if (0 == mo && yr) {
    					yr -= 1; mo = 11;
    					jQuery("select[name=month]", datepicker).get(0).selectedIndex = 11;
    					jQuery("select[name=year]", datepicker).get(0).selectedIndex = yr;
    				} else {
    					mo -= 1;
    					jQuery("select[name=month]", datepicker).get(0).selectedIndex = mo;
    				}
    			} else if (e && jQuery(e.target).hasClass('nextMonth')) {
    				if (11 == mo && yr + 1 < yrs) {
    					yr += 1; mo = 0;
    					jQuery("select[name=month]", datepicker).get(0).selectedIndex = 0;
    					jQuery("select[name=year]", datepicker).get(0).selectedIndex = yr;
    				} else { 
    					mo += 1;
    					jQuery("select[name=month]", datepicker).get(0).selectedIndex = mo;
    				}
    			}
    			
    			// maybe hide buttons
    			if (0 == mo && !yr) jQuery("span.prevMonth", datepicker).hide(); 
    			else jQuery("span.prevMonth", datepicker).show(); 
    			if (yr + 1 == yrs && 11 == mo) jQuery("span.nextMonth", datepicker).hide(); 
    			else jQuery("span.nextMonth", datepicker).show(); 
    			
    			// clear the old cells
    			var cells = jQuery("tbody td", datepicker).unbind().empty().removeClass('date');
    			
    			// figure out what month and year to load
    			var m = jQuery("select[name=month]", datepicker).val();
    			var y = jQuery("select[name=year]", datepicker).val();
    			var d = new Date(y, m, 1);
    			var startindex = d.getDay();
    			var numdays = monthlengths[m];
    			
    			// http://en.wikipedia.org/wiki/Leap_year
    			if (1 == m && ((y%4 == 0 && y%100 != 0) || y%400 == 0)) numdays = 29;
    			
    			// test for end dates (instead of just a year range)
    			if (opts.startdate.constructor == Date) {
    				var startMonth = opts.startdate.getMonth();
    				var startDate = opts.startdate.getDate();
    			}
    			if (opts.enddate.constructor == Date) {
    				var endMonth = opts.enddate.getMonth();
    				var endDate = opts.enddate.getDate();
    			}
    			
    			// walk through the index and populate each cell, binding events too
    			for (var i = 0; i < numdays; i++) {
    			
    				var cell = jQuery(cells.get(i+startindex)).removeClass('chosen');
    				
    				// test that the date falls within a range, if we have a range
    				if ( 
    					(yr || ((!startDate && !startMonth) || ((i+1 >= startDate && mo == startMonth) || mo > startMonth))) &&
    					(yr + 1 < yrs || ((!endDate && !endMonth) || ((i+1 <= endDate && mo == endMonth) || mo < endMonth)))) {
    				
    					cell
    						.text(i+1)
    						.addClass('date')
    						.hover(
    							function () { jQuery(this).addClass('over'); },
    							function () { jQuery(this).removeClass('over'); })
    						.click(function () {
    							var chosenDateObj = new Date(jQuery("select[name=year]", datepicker).val(), jQuery("select[name=month]", datepicker).val(), jQuery(this).text());
    							closeIt(el, datepicker, chosenDateObj);
    						});
    						
    					// highlight the previous chosen date
    					if (i+1 == chosendate.getDate() && m == chosendate.getMonth() && y == chosendate.getFullYear()) cell.addClass('chosen');
    				}
    			}
    		}
    		
    		/** closes the datepicker **/
    		// sets the currently matched input element's value to the date, if one is available
    		// remove the table element from the DOM
    		// indicate that there is no datepicker for the currently matched input element
    		function closeIt (el, datepicker, dateObj) { 
    			if (dateObj && dateObj.constructor == Date)
    				el.val(jQuery.fn.simpleDatepicker.formatOutput(dateObj));
    			datepicker.remove();
    			datepicker = null;
    			jQuery.data(el.get(0), "simpleDatepicker", { hasDatepicker : false });
    		}
    
            // iterate the matched nodeset
            return this.each(function() {
    			
                // functions and vars declared here are created for each matched element. so if
                // your functions need to manage or access per-node state you can defined them
                // here and use $this to get at the DOM element
    			
    			if ( jQuery(this).is('input') && 'text' == jQuery(this).attr('type')) {
    
    				var datepicker; 
    				jQuery.data(jQuery(this).get(0), "simpleDatepicker", { hasDatepicker : false });
    				
    				// open a datepicker on the click event <-- Modified by Zarin. 
    				jQuery(this).parent().find('.calpic').click(function (ev) { //<-- Binds the click to the inputs .calpic siblings
    					console.log(ev.target)	 
    					var $this = jQuery(ev.target).parent().find('input'); //<-- Ties the datepicker back to the input. This is required after changing the event target.
    					
    					if (false == jQuery.data($this.get(0), "simpleDatepicker").hasDatepicker) {
    						
    						// store data telling us there is already a datepicker
    						jQuery.data($this.get(0), "simpleDatepicker", { hasDatepicker : true });
    						
    						// validate the form's initial content for a date
    						var initialDate = $this.val();
    						
    						if (initialDate && dateRegEx.test(initialDate)) {
    							var chosendate = new Date(initialDate);
    						} else if (opts.chosendate.constructor == Date) {
    							var chosendate = opts.chosendate;
    						} else if (opts.chosendate) {
    							var chosendate = new Date(opts.chosendate);
    						} else {
    							var chosendate = today;
    						}
    							
    						// insert the datepicker in the DOM
    						datepicker = newDatepickerHTML();
    						jQuery("body").prepend(datepicker);
    						
    						// position the datepicker
    						var elPos = findPosition($this.get(0));
    						var x = (parseInt(opts.x) ? parseInt(opts.x) : 0) + elPos[0];
    						var y = (parseInt(opts.y) ? parseInt(opts.y) : 0) + elPos[1];
    						jQuery(datepicker).css({ position: 'absolute', left: x, top: y });
    					
    						// bind events to the table controls
    						jQuery("span", datepicker).css("cursor","pointer");
    						jQuery("select", datepicker).bind('change', function () { loadMonth (null, $this, datepicker, chosendate); });
    						jQuery("span.prevMonth", datepicker).click(function (e) { loadMonth (e, $this, datepicker, chosendate); });
    						jQuery("span.nextMonth", datepicker).click(function (e) { loadMonth (e, $this, datepicker, chosendate); });
    						jQuery("span.today", datepicker).click(function () { closeIt($this, datepicker, new Date()); });
    						jQuery("span.close", datepicker).click(function () { closeIt($this, datepicker); });
    						
    						// set the initial values for the month and year select fields
    						// and load the first month
    						jQuery("select[name=month]", datepicker).get(0).selectedIndex = chosendate.getMonth();
    						jQuery("select[name=year]", datepicker).get(0).selectedIndex = Math.max(0, chosendate.getFullYear() - opts.startyear);
    						loadMonth(null, $this, datepicker, chosendate);
    					}
    					
    				});
    			}
    
            });
    
        };
    
        // finally, I like to expose default plugin options as public so they can be manipulated.  one
        // way to do this is to add a property to the already-public plugin fn
    
    	jQuery.fn.simpleDatepicker.formatOutput = function (dateObj) {
    		return (dateObj.getMonth() + 1) + "/" + dateObj.getDate() + "/" + dateObj.getFullYear();	
    	};
    	
    	jQuery.fn.simpleDatepicker.defaults = {
    		// date string matching /^\d{1,2}\/\d{1,2}\/\d{2}|\d{4}$/
    		chosendate : today,
    		
    		// date string matching /^\d{1,2}\/\d{1,2}\/\d{2}|\d{4}$/
    		// or four digit year
    		startdate : today.getFullYear(), 
    		enddate : today.getFullYear() + 1,
    		
    		// offset from the top left corner of the input element
    		x : 18, // must be in px
    		y : 18 // must be in px
    	};
    
    })(jQuery);

  3. #3
    Visible Ninja bronze trophy
    JeffWalden's Avatar
    Join Date
    Sep 2002
    Location
    Los Angeles
    Posts
    1,709
    Mentioned
    5 Post(s)
    Tagged
    0 Thread(s)
    Thanks much! This helps immensely.

    I'll tweak it from here but you've put me in the right direction.
    TAKE A WALK OUTSIDE YOUR MIND.

  4. #4
    SitePoint Addict Zarin Denatrose's Avatar
    Join Date
    Jan 2009
    Location
    Surrey BC, Canada
    Posts
    309
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Good to hear, I'm glad I could help!


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •