marcy_sss
07-04-2008, 12:19 AM
Hello all,
I am currently stuck on a Javascript problem that expands upon Seth Duffey's "A More Accessible Map" (http://www.alistapart.com/articles/cssmaps) from a few years ago. I emailed him about it, but still couldn't come up with a solution as this pushes the limits of my JS knowledge.
Basically, I am using his map technique, which uses a Javascript object literal (mapMaker). If you are not familiar with it, it converts a definition list <DL> into points on a map, placing each one with CSS. They are "hidden" until you roll over them. The benefit is that the map image and data are totally separate, and you can add/delete/edit points easily. It is meant to be simple to fit in with the design/project scope we sold to the client.
Here is where I am expanding on it: I have a list of the places on the map elsewhere on the page (in a table dynamically populated with JS, so I only have to edit the <DL>). When an item in the list outside of the map is hovered, I want the corresponding point on the map to become active - mapMaker.showTooltip(). Just look at the page and you will see what I mean (http://client.deicreative.com/row5/neighborhood.php?page=map).
What I need to do is call mapMaker.showTooltip() or mapMaker.addEvt() on mouseover of an item outside of the map (which already has a unique ID based on the corresponding item inside of the map). I am having a hell of a time trying to make use of any of the functions in the script. None of the properties seem to be available when I need them, including 'currentLocation'. Any idea how to call a function that isn't really available? Do I have to rewrite the script to be a class instead of an object literal?
I would really appreciate some help on this one - I find it hard to believe that no one has ever wanted to solve this same problem in the two years since the article was published.
Here is the page where I am using it:
http://client.deicreative.com/row5/neighborhood.php?page=map
Here is some code I wrote:
// This function dynamically populates a table outside of the map:
function mapList(id) {
var dlID = document.getElementById(id).innerHTML;
var tdID = id + 'tableitem';
document.write('<td id="'+tdID+'" class="mapList" onmouseover="" onmouseout="">' + dlID + '</td>');
}
Here is a copy of mapevents.js, taken from A List Apart:
var mapMaker = {
offsetX: -25, // tooltip X offset
offsetY: 7, // tooltip Y offset
element: false,
DLs: false,
DTs: false,
on: false,
/* constructor - sets events */
init: function(){
var i=0;
var ii=0;
var currentLocation = 0;
mapMaker.DLs = document.getElementsByTagName('dl');
mapMaker.DTs = document.getElementsByTagName('dt');
// only loop thru items once
if( mapMaker.on == false ){
//loop through each DL on page
while (mapMaker.DLs.length > i) {
//only affect DLs with a class of 'map'
if (mapMaker.DLs[i].className == 'map' ){
//change map DL class, this way map is text only without javascript enabled
mapMaker.DLs[i].className = 'map on';
//strip whitespace
mapMaker.stripWhitespace(mapMaker.DLs[i]);
mapMaker.stripWhitespace(mapMaker.DTs[i]);
// loop thru all DT elements
while (mapMaker.DTs.length > ii){
//current Location
currentLocation = mapMaker.DTs[ii].firstChild;
// add events to links
mapMaker.addEvt(currentLocation,'mouseover',mapMaker.showTooltip);//displays tooltip on mouse over
mapMaker.addEvt(currentLocation,'mouseout',mapMaker.hideTooltip);//hide tooltip on mouse out
mapMaker.addEvt(currentLocation,'focus',mapMaker.showTooltip);//display tooltip on focus, for added keyboard accessibility
mapMaker.addEvt(currentLocation,'blur',mapMaker.hideTooltip);//hide tooltip on focus, for added keyboard accessibility
ii++;
};
ii=0;
};
i++;
};
mapMaker.on = true;
};
},
/* SHOW TOOLTIP */
showTooltip: function() {
var evt = this;
var i = 0;
//Find DD to display - based on currently hovered anchor move to parent DT then next sibling DD
var objid = evt.parentNode.nextSibling;
mapMaker.element = objid;//set for the hideTooltip
//get width and height of background map
var mapWidth = objid.parentNode.offsetWidth;
var mapHeight = objid.parentNode.offsetHeight;
//get width and height of the DD
var toopTipWidth = objid.offsetWidth;
var toopTipHeight = objid.offsetHeight;
//figure out where tooltip should be places based on point location
var newX = evt.offsetLeft + mapMaker.offsetX;
var newY = evt.offsetTop + mapMaker.offsetY;
//check if tooltip fits map width
if ((newX + toopTipWidth) > mapWidth) {
objid.style.left = newX-toopTipWidth-24 + 'px';
} else {
objid.style.left = newX + 'px';
};
//check if tooltip fits map height
if ((newY + toopTipHeight) > mapHeight) {
objid.style.top = newY-toopTipHeight-14 + 'px';
} else {
objid.style.top = newY + 'px';
};
},
/* HIDE TOOLTIP */
hideTooltip: function() {
mapMaker.element.style.left = '-9999px';
},
addEvt: function(element, type, handler) {
// assign each event handler a unique ID
if (!handler.$$guid) handler.$$guid = mapMaker.addEvt.guid++;
// create a hash table of event types for the element
if (!element.events) element.events = {};
// create a hash table of event handlers for each element/event pair
var handlers = element.events[type];
if (!handlers) {
handlers = element.events[type] = {};
// store the existing event handler (if there is one)
if (element["on" + type]) {
handlers[0] = element["on" + type];
};
};
// store the event handler in the hash table
handlers[handler.$$guid] = handler;
// assign a global event handler to do all the work
element["on" + type] = mapMaker.handleEvent;
},
handleEvent: function(event) {
var returnValue = true;
// grab the event object (IE uses a global event object)
event = event || mapMaker.fixEvent(window.event);
// get a reference to the hash table of event handlers
var handlers = this.events[event.type];
// execute each event handler
for (var i in handlers) {
this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) {
returnValue = false;
};
};
return returnValue;
},
fixEvent: function(event) {
// add W3C standard event methods
event.preventDefault = mapMaker.fixEvent.preventDefault;
event.stopPropagation = mapMaker.fixEvent.stopPropagation;
return event;
},
stripWhitespace: function( el ){
for(var i = 0; i < el.childNodes.length; i++){
var node = el.childNodes[i];
if( node.nodeType == 3 && !/\S/.test(node.nodeValue)) {
node.parentNode.removeChild(node);
};
};
}
};
mapMaker.fixEvent.preventDefault = function() {this.returnValue = false;};
mapMaker.fixEvent.stopPropagation = function() {this.cancelBubble = true;};
mapMaker.addEvt.guid = 1;
/* LOAD SCRIPT */
/* for Mozilla */
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", mapMaker.init, null);
};
/* for Internet Explorer */
/*@cc_on @*/
/*@if (@_win32)
document.write("<script defer src=ie_onload.js><"+"/script>");
/*@end @*/
/* for other browsers */
mapMaker.addEvt( window, 'load', mapMaker.init);
I look forward to hearing SOMETHING! Thanks for any time you care to contribute!
Cheers,
Marcy
I am currently stuck on a Javascript problem that expands upon Seth Duffey's "A More Accessible Map" (http://www.alistapart.com/articles/cssmaps) from a few years ago. I emailed him about it, but still couldn't come up with a solution as this pushes the limits of my JS knowledge.
Basically, I am using his map technique, which uses a Javascript object literal (mapMaker). If you are not familiar with it, it converts a definition list <DL> into points on a map, placing each one with CSS. They are "hidden" until you roll over them. The benefit is that the map image and data are totally separate, and you can add/delete/edit points easily. It is meant to be simple to fit in with the design/project scope we sold to the client.
Here is where I am expanding on it: I have a list of the places on the map elsewhere on the page (in a table dynamically populated with JS, so I only have to edit the <DL>). When an item in the list outside of the map is hovered, I want the corresponding point on the map to become active - mapMaker.showTooltip(). Just look at the page and you will see what I mean (http://client.deicreative.com/row5/neighborhood.php?page=map).
What I need to do is call mapMaker.showTooltip() or mapMaker.addEvt() on mouseover of an item outside of the map (which already has a unique ID based on the corresponding item inside of the map). I am having a hell of a time trying to make use of any of the functions in the script. None of the properties seem to be available when I need them, including 'currentLocation'. Any idea how to call a function that isn't really available? Do I have to rewrite the script to be a class instead of an object literal?
I would really appreciate some help on this one - I find it hard to believe that no one has ever wanted to solve this same problem in the two years since the article was published.
Here is the page where I am using it:
http://client.deicreative.com/row5/neighborhood.php?page=map
Here is some code I wrote:
// This function dynamically populates a table outside of the map:
function mapList(id) {
var dlID = document.getElementById(id).innerHTML;
var tdID = id + 'tableitem';
document.write('<td id="'+tdID+'" class="mapList" onmouseover="" onmouseout="">' + dlID + '</td>');
}
Here is a copy of mapevents.js, taken from A List Apart:
var mapMaker = {
offsetX: -25, // tooltip X offset
offsetY: 7, // tooltip Y offset
element: false,
DLs: false,
DTs: false,
on: false,
/* constructor - sets events */
init: function(){
var i=0;
var ii=0;
var currentLocation = 0;
mapMaker.DLs = document.getElementsByTagName('dl');
mapMaker.DTs = document.getElementsByTagName('dt');
// only loop thru items once
if( mapMaker.on == false ){
//loop through each DL on page
while (mapMaker.DLs.length > i) {
//only affect DLs with a class of 'map'
if (mapMaker.DLs[i].className == 'map' ){
//change map DL class, this way map is text only without javascript enabled
mapMaker.DLs[i].className = 'map on';
//strip whitespace
mapMaker.stripWhitespace(mapMaker.DLs[i]);
mapMaker.stripWhitespace(mapMaker.DTs[i]);
// loop thru all DT elements
while (mapMaker.DTs.length > ii){
//current Location
currentLocation = mapMaker.DTs[ii].firstChild;
// add events to links
mapMaker.addEvt(currentLocation,'mouseover',mapMaker.showTooltip);//displays tooltip on mouse over
mapMaker.addEvt(currentLocation,'mouseout',mapMaker.hideTooltip);//hide tooltip on mouse out
mapMaker.addEvt(currentLocation,'focus',mapMaker.showTooltip);//display tooltip on focus, for added keyboard accessibility
mapMaker.addEvt(currentLocation,'blur',mapMaker.hideTooltip);//hide tooltip on focus, for added keyboard accessibility
ii++;
};
ii=0;
};
i++;
};
mapMaker.on = true;
};
},
/* SHOW TOOLTIP */
showTooltip: function() {
var evt = this;
var i = 0;
//Find DD to display - based on currently hovered anchor move to parent DT then next sibling DD
var objid = evt.parentNode.nextSibling;
mapMaker.element = objid;//set for the hideTooltip
//get width and height of background map
var mapWidth = objid.parentNode.offsetWidth;
var mapHeight = objid.parentNode.offsetHeight;
//get width and height of the DD
var toopTipWidth = objid.offsetWidth;
var toopTipHeight = objid.offsetHeight;
//figure out where tooltip should be places based on point location
var newX = evt.offsetLeft + mapMaker.offsetX;
var newY = evt.offsetTop + mapMaker.offsetY;
//check if tooltip fits map width
if ((newX + toopTipWidth) > mapWidth) {
objid.style.left = newX-toopTipWidth-24 + 'px';
} else {
objid.style.left = newX + 'px';
};
//check if tooltip fits map height
if ((newY + toopTipHeight) > mapHeight) {
objid.style.top = newY-toopTipHeight-14 + 'px';
} else {
objid.style.top = newY + 'px';
};
},
/* HIDE TOOLTIP */
hideTooltip: function() {
mapMaker.element.style.left = '-9999px';
},
addEvt: function(element, type, handler) {
// assign each event handler a unique ID
if (!handler.$$guid) handler.$$guid = mapMaker.addEvt.guid++;
// create a hash table of event types for the element
if (!element.events) element.events = {};
// create a hash table of event handlers for each element/event pair
var handlers = element.events[type];
if (!handlers) {
handlers = element.events[type] = {};
// store the existing event handler (if there is one)
if (element["on" + type]) {
handlers[0] = element["on" + type];
};
};
// store the event handler in the hash table
handlers[handler.$$guid] = handler;
// assign a global event handler to do all the work
element["on" + type] = mapMaker.handleEvent;
},
handleEvent: function(event) {
var returnValue = true;
// grab the event object (IE uses a global event object)
event = event || mapMaker.fixEvent(window.event);
// get a reference to the hash table of event handlers
var handlers = this.events[event.type];
// execute each event handler
for (var i in handlers) {
this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) {
returnValue = false;
};
};
return returnValue;
},
fixEvent: function(event) {
// add W3C standard event methods
event.preventDefault = mapMaker.fixEvent.preventDefault;
event.stopPropagation = mapMaker.fixEvent.stopPropagation;
return event;
},
stripWhitespace: function( el ){
for(var i = 0; i < el.childNodes.length; i++){
var node = el.childNodes[i];
if( node.nodeType == 3 && !/\S/.test(node.nodeValue)) {
node.parentNode.removeChild(node);
};
};
}
};
mapMaker.fixEvent.preventDefault = function() {this.returnValue = false;};
mapMaker.fixEvent.stopPropagation = function() {this.cancelBubble = true;};
mapMaker.addEvt.guid = 1;
/* LOAD SCRIPT */
/* for Mozilla */
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", mapMaker.init, null);
};
/* for Internet Explorer */
/*@cc_on @*/
/*@if (@_win32)
document.write("<script defer src=ie_onload.js><"+"/script>");
/*@end @*/
/* for other browsers */
mapMaker.addEvt( window, 'load', mapMaker.init);
I look forward to hearing SOMETHING! Thanks for any time you care to contribute!
Cheers,
Marcy