WeLoveCSS Logo
Home Profile Members Search Rules Help New Posts



WeLoveCSS > PROGRAMMING LANGUAGES > Scripting and Server Side > [SOLVED] Javascript Cross Browser Compatibility

Reply
  Thread Tools Display Modes
Old 28th September 13, 01:42 AM   #1
meesa
WLC Mod
 
meesa's Avatar
 
Join Date: Jul 2009
Location: Milky Way Galaxy
Posts: 3,711
Default Javascript Cross Browser Compatibility

I've always considered JavaScript a cross browser compatible language. What happens in one will happen in the other. However, that illusion was shattered by one of my recent projects: turning an input field into a calendar field.

The idea is simple: When I click on the input field, an arrow appears next to the input, and if you click on that arrow, then a calendar is created based on the current date, or the date in the input box. If you click on a new date, then the input box date is changed.

Code:
<input type="text" name="date" id="date" onfocus="createCalElem(this)" />
The hold up comes in IE (Even IE11, sadly) that for some reason allows tables, and TDs to have the focus. This is a problem because the onblur element is what closes the calendar, so the focus would be taken from the parent DIV (I used a negative tabindex to accomplish this) to a child element that should never have gained focus. So IE would close the calendar before the date was selected.

A simple solution, get the document.activeElement along with parent.contains to see if the newly focused element is apart of the calendar.

There's one more snag though. The calendar arrow needs to disappear when the input looses focus as well. In IE, this works perfectly, however, in FF, it doesn't change the .activeElement until after the onblur event finishes. To rectify this, I had to use a setTimeout operation inside the onblur event. 5ms is all the time I needed on my computer, so I doubled it for slower machines.

Code:
input.setAttribute("onblur", "setTimeout('destroyCal(document.getElementById(\\\"" + id + "\\\"))', 10)");
The interesting part is that the actual calendar doesn't need this timeout because FF doesn't "blur" anything when an element is clicked, and IE already knows what the new element is.

Below is my fully functional calendar code. Suggestions / improvements are always welcome.

Code:
<!-- HTML -->
<input type="text" name="date" id="date" onfocus="createCalElem(this)" />

<!-- JavaScript -->

function createCalElem(input){

	// Find out where the calendar DD arrow needs to go, and it's ID.
	var x = (input.offsetLeft + input.offsetWidth) + "px";
	var y = (input.offsetTop + 3) + "px";
	var h = (input.offsetHeight - 9) + "px";
	var w = "14px";
	var id = input.id + "Cal";
	
	// Create the element and assign the attributes.
	var cal = document.createElement("div");
	cal.setAttribute("id", id);
	cal.setAttribute("class", "calButton");
	cal.setAttribute("onclick", "ddMakeCal('" + input.id + "', '" + id + "')");
	cal.setAttribute("tabindex", "-1");
	cal.setAttribute("onblur", "destroyCal(this)");
	
	cal.style.width = w;
	cal.style.lineHeight = h;
	cal.style.position = "absolute";
	cal.style.top = y;
	cal.style.left = x;
	
	// Add the DD arrow to the DIV
	cal.appendChild(document.createTextNode(String.fromCharCode(9660)));
	
	// Set input to have onblur event so arrow will disappear onblur. Timeout is necessary for FF.
	input.setAttribute("onblur", "setTimeout('destroyCal(document.getElementById(\\\"" + id + "\\\"))', 10)");
	
	// Add the element to the body.
	document.body.appendChild(cal);
}

function ddMakeCal(input, id){
	var date = new Date();
	var input = document.getElementById(input);
	
	// If there's nothing in the input, get current month and year
	if(input.value.length < 1){
		year = date.getFullYear();
		month = date.getMonth();
	}
	// Else extract it from the input
	else {
		year = parseInt(input.value.substr(0, 4));
		month = parseInt(input.value.substr(5,2), 10) - 1;
	}
	
	makeCal(month, year, id);
}

function makeCal(month, year, div){
	// Reassign div to be an element instead of an ID name
	var calDIV = document.getElementById(div);
	// Remove top level onclick
	calDIV.removeAttribute("onclick");
	
	// Get what day the first date of the current month was.
	var firstDay = ((Date.UTC(year, month, 01) - Date.UTC(2006, 0, 01)) / 1000 / 60 / 60 / 24) % 7;
		if(firstDay < 0){ firstDay += 7; } // For dates before 2006. Takes -3 -> 4; -6 -> 1 (Because of negative, this is necessary)
	
	// Get how many days were in the previous month
	var numLastMon = new Date(year, month, 00).getDate();
	// Get how many days were in this month
	var numThisMon = new Date(year, month+1, 00).getDate();
	// Months
	var months = ["January","February","March","April","May","June","July","August","September","October","November","December"];
	// Store month name
	var monthName = months[month];
	// Get next month and year
	var nextMon  = (month == 11 ? 0 : month+1);
	var nextYear = (nextMon == 0 ? year+1 : year);
	// Get previous month and year
	var preMon  = (month == 0 ? 11 : month-1);
	var preYear = (preMon == 11 ? year -1: year);	
	// Used to store the current day, month, and year	
	var curDay  = (new Date()).getDate();
	var curMon  = (new Date()).getMonth();
	var curYear = (new Date()).getFullYear();
	// Used to store which day is being written
	var day;
	
	// Start creating the table
	var cal = "<table cellpadding='0' cellspacing='0'>\n" + 
			  "<tr id='month'>\n" +
			  "<th onclick=\"makeCal(" + preMon + ", " + preYear + ", '" + div + "')\">«</th>\n" +
			  "<th colspan='5'>" + monthName + " " + year + "</th>\n" +
			  "<th onclick=\"makeCal(" + nextMon + ", " + nextYear + ", '" + div + "')\">»</th>\n" +
			  "</tr>\n" + 
			  "<tr id='day'>\n" +
			  "<th>S</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th>\n" +
			  "</tr>\n" +
			  "<tr>\n"

	// Write previous month if necessary
	for(day = (numLastMon + 1) - firstDay; day <= numLastMon; day++){
		cal += ("<td class=\"otherMon\" onclick=\"setDate('" + makeDate(year, month+1, day) + "', '" + div +"')\">" + day + "</td>");
	}
	// Write the current month, writing a new table row every 7 days.
	for(day = 1; day <= numThisMon; day++){
		var dd = (day < 10 ? "0" : "") + day;
		var id = "";
		if(isChosenDate(makeDate(year, month+1, day), div)){
			id = " id='chosen'";
		}
		else if(day == curDay && month == curMon && year == curYear){
			id = " id='curDay'";
		}
		cal += ("<td" + id + " onclick=\"setDate('" + makeDate(year, month+1, day) + "', '" + div +"')\">" + day + "</td>");
		if((day + firstDay) % 7 == 0){
			cal += ("</tr>\n<tr>\n");
		}
	}
	// Write the next month if necessary. j = day due to day being needed for calculation.
	for(var j = 1; ((day-1) + firstDay + j) % 7 != 1; j++){
	var dd = (j < 10 ? "0" : "") + j;
		cal += ("<td class=\"otherMon\" onclick=\"setDate('" +makeDate(year, month+1, j) + "', '" + div +"')\">" + j + "</td>");
	}

	// Finish writing the table
	cal += "</tr>\n" + 
		   "<tr id='today'>\n" + 
		   "<td colspan='7' onclick=\"setDate('" + makeDate(curYear, curMon+1, curDay) + "', '" + div +"')\">Today: " + 
						makeDate(curYear, curMon+1, curDay) + "</td>\n" +
		   "</tr>" +
		   "</table>";

	// Write calendar
	calDIV.innerHTML = cal;
	// Make sure that the calendar has the class set for calendar
	calDIV.className = "calendar";
	// Clear old inline styles so they don't interfere
	calDIV.style.width = "auto";
	calDIV.style.lineHeight = "normal";
	// Focus on element.
	calDIV.focus();
}

function makeDate(year, month, day){
	var mm = (month < 10 ? "0" : "") + parseInt(month); // parseInt is necessary in case a "06" is passed, it doesn't come back 006.
	var dd = (day   < 10 ? "0" : "") + parseInt(day);
	return year + "-" + mm + "-" + dd;
}

function destroyCal(elem){
	// All of this is needed for cross browser compatibility.
	/* First, we check to see if the newly focused element is a child of elem. In FF, this is always false, however
	** FF also doesn't change the focus when one of the dates is clicked, therefore this event is only fired when the input
	** is focused on. FF also just considers nothing having focus during this brief time. IE on the other hand will change the 
	** focus somehow to part of the table, so it will a times return true. .activeElement is also the newly focused element.
	*/
	if(!elem.contains(document.activeElement)){
		elem.parentNode.removeChild(elem);
	}
	/* Due to IE's issue of changing the focus to the table, this changes the focus back to the DIV thereby allowing the onblur
	** event to fire again. This is important in the prevention of there being two calendars, or a calendar and a DD arrow.
	*/
	else{
		elem.focus();
	}
}

function setDate(date, id){
	// Get ID of input by stripping the last three characters from the passed ID (Last 3 - "Cal")
	var input = document.getElementById(id.substr(0, id.length - 3));
	input.value = date;
	
	//Focus on input now.
	input.focus();
}

function isChosenDate(date, id){
	// Get ID of input by stripping the last three characters from the passed ID (Last 3 - "Cal")
	var input = document.getElementById(id.substr(0, id.length - 3));
	
	// Extract year, month, and date assuming there is something in the field
	if(input.value.length < 1){
		return false
	}
	
	if(date == input.value){
		return true
	}
	
	return false;
}
__________________
Praise be to the Lord God for the ability to learn, the capability to analyze, and the time to help users on this forum.
meesa is offline   Reply With Quote
Old 20th April 15, 04:49 AM   #2
dannipaff
WLC Member
 
Join Date: Apr 2015
Posts: 1
Default Re: Javascript Cross Browser Compatibility

This article is going to highlight browser compatibility issues and tell you how to ... Voila, a cross-browser function to get an element by its ID and it's not even ...
__________________

To view links or images in signatures your post count must be 5 or greater. You currently have 0 posts.
dannipaff is offline   Reply With Quote
Reply


Thread Tools
Display Modes
Linear Mode Linear Mode

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT. The time now is 05:37 PM.



Home | Advertise | Contact Us | Top
Home | Advertise | Contact Us | Top

Copyright© 2006 WeLoveCSS.com. All Rights Reserved.
Powered by vBulletin Version 3.8.4 Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.