Are HTML Image Maps still used?
Yes, people do still use image maps. An alternative would be to position elements using absolute positioning and CSS but that's not necessarily better. It also doesn't allow you to have shapes like in image maps
Are image maps (html tag - map) crawled by google?
I've been wondering this recently too and found this within the Google developer instructions around enterprise site search - https://developers.google.com/search-appliance/documentation/68/admin_crawl/Introduction#areatag
This suggests that the Google Search Appliance crawler will not follow these links. Whether that is also true for regular Googlebot I don't know but it would seem likely...
More modern image maps?
Responsive SVG solution or classic image map
After taking Paulie_D's comment about SVG into account, I wrote an alternative using SVG to the classic image map. Both work fine, but the SVG version clearly wins when it comes to responsiveness. Both versions have a connection between the anchors and the respective tooltip using the href
-attribute. Both solutions work with vanilla JavaScript, without an extra library.
SVG version
Advantages
- responsive
- tooltips can be placed easily using JavaScript
HTML
<svg id="map" version="1.1" viewBox="0 0 300 300">
<image width="300" height="300" xlink:href="http://placehold.it/300"/>
<a xlink:href="#t_1">
<rect x="50" y="50" width="50" height="50" />
</a>
<a xlink:href="#t_2">
<rect x="150" y="150" width="50" height="50" />
</a>
</svg>
<div class="t" id="t_1">Tooltip 1</div>
<div class="t" id="t_2">Tooltip 2</div>
CSS
html, body {
width: 100%;
margin: 0;
padding: 0;
}
svg {
display: block;
width: 80%;
max-width: 300px;
margin: 0 auto;
}
svg rect {
fill: white;
opacity: 0.1;
transition: all 0.2s linear;
}
svg rect:hover {
opacity: 0.8;
}
.t {
opacity: 0;
position: absolute;
left: 0;
top: 0;
transition: opacity 0.4s linear;
}
.t.active {
opacity: 1;
}
JavaScript*
var map = document.getElementById('map');
var areas = map.getElementsByTagName('a');
var offset = { left: 30, top: 70 };
for (var i = 0; i < areas.length; i++) {
areas[i].onmouseover = function() {
// get child element
var c = this.firstElementChild;
// get tooltip
var t = document.getElementById(this.getAttribute('xlink:href').substr(1));
// set styles
t.style.left = (map.offsetLeft + parseInt(c.getAttribute('x')) + offset.left) + 'px';
t.style.top = (map.offsetTop + parseInt(c.getAttribute('y')) + offset.top) + 'px';
// show it
t.classList.toggle('active');
}
areas[i].onmouseout = function() {
// get tooltip
var t = document.getElementById(this.getAttribute('xlink:href').substr(1));
// hide it
t.classList.toggle('active');
}
}
Notes
- the positioning could be improved, it's just to show a direction
Demo
Try before buy
Classic image map version
HTML
<img src="http://placehold.it/300" alt="Sample Image" usemap="#map">
<map id="map" name="map">
<area shape="rect" coords="0,0,50,50" href="#t_1" alt="Tip 1" data-left="80px" data-top="80px" />
<area shape="rect" coords="100,100,150,150" href="#t_2" alt="Tip 2" data-left="180px" data-top="180px" />
</map>
<div class="t" id="t_1">Tooltip 1</div>
<div class="t" id="t_2">Tooltip 2</div>
CSS
.t {
opacity: 0;
position: absolute;
left: 0;
top: 0;
transition: opacity 0.4s linear;
}
.t.active {
opacity: 1;
}
JavaScript*
var areas = document.getElementById('map').children;
for (var i = 0; i < areas.length; i++) {
areas[i].onmouseover = function() {
var t = document.getElementById(this.hash.substr(1));
t.style.left = this.dataset.left;
t.style.top = this.dataset.top;
t.classList.toggle('active');
}
areas[i].onmouseout = function() {
var t = document.getElementById(this.hash.substr(1));
t.classList.toggle('active');
}
}
Notes
- attaching the position using the
data-*
-attributes, decouples the JavaScript (unfortunately you can't useoffsetLeft/Top
and determine the position based on thearea
-element) - you could however calculate it by using thecoords
-attribute - the JavaScript code could be improved (for example store tooltips instead of re-query them all the time)
Demo
Try before buy
* In both examples the JavaScript could be improved, e.g. store tooltip elements in a variable instead of re-query them all the time.
Image maps and HTML5
Why is using image maps for navigation inexcusable? It's a tool like any other; it has a time and place. Using imagemaps with javascript enhancements is backward compatible, degrades gracefully, and has 100% browser support. They don't need a plugin like flash.They've been supported practically since the dawn of the web browser. Just because something's old doesn't mean it isn't useful; quite the opposite, it means it's well supported.
I wrote a jquery plugin called ImageMapster to add effects to imagemaps so you could create interactive images without using flash. It would be easy to implement a tool that had the same functonality without Javascript support by replacing with a list in those cases. Personally, I think trying to write for the web without javascript is like trying to drive a car without tires. 99% of the web doesn't work without it any more. This isn't 1995. But if you really are concerned, the nice thing about imagemaps is the basic navigation functionality still works. There's no way to accomplish that just with CSS -- not even CSS3 if you have irregular shaped areas.
Is it possible to use HTML image maps within an A-Frame scene?
The <map>
tag places the areas above an image. It won't project the areas onto a three dimentional object just like that.
I'd recommend an approach where you re-use the areas defined in the <map>
tag within an a-frame custom component.
tldr:
I've made a component that seems to do the job, and is fairly simple in use:
<a-image material="src: #texture" asourcemap="#some-map"></a-image>
<!-- somewhere else -->
<map id="some-map">
<area shape="rect" alt="rectangle" coords="0,0, 50,50" href="rect.html">
<area shape="polygon" alt="poly" coords="0,0, 40,50, 25,0">
</map>
It should work well with the href
attribute, but also the <a-image>
will emit a signal with the clicked area name.
You can see it working with 3D planes and cylinders here.
end of tldr
0. Gathering <map>
data
Simple parsing. Grab the <map>
element, iterate through all children, and collect their data with getAttribute()
:
var map = document.querySelector(selector);
for (let area of map.children) {
// area.getAttribute("href") - href attribute
// area.getAttribute("alt") - alt name
// area.getAttribute("coords") - coordinates array.
}
Store them for later use. The coordinates are comma separated strings, so you may need to parseInt()
them, manage the order (i.e. [[x1,x1], [x2,y2], [x3, y3]]
)
1. Make the a-frame entity interactable
React on clicks, and what's more important - check where the click occurred:
this.el.addEventListener("click", evt => {
var UVPoint = evt.detail.intersection.uv
})
UV mapping will help us determine which point on the texture was clicked. The UV ranges from <0, 1>, so we will need to re-scale the UVPoint:
// may need waiting for "model-loaded"
let mesh = this.el.getObject3D("mesh")
// this may not be available immidiately
let image = mesh.material.map.image
let x_on_image = UVPoint * image.width
// the y axis goes from <1, 0>
let y_on_image = image.height - UVPoint * image.heigth
So hey, we got the area coordinates and the point coordinates!
There is only one thing left:
2. Determining if an area was clicked
No need to re-invent the wheel here. This SO question on checking if a point is inside a polygon has a simple inside(point, polygon)
function. Actually we have everything we need, so the last thing we do is:
- iterate through the polygons
- check if the clicked point is inside any of the polygons
- if positive - do your thing
like this:
var point = [x_on_texture, y_on_texture]
for (var i = 0; i < polygons.length; i++) {
// polygons need to be [[x1, y1], [x2, y2],...[xn, yn]] here
if (inside(point, polygons[i]) {
console.log("polygon", i, "clicked!")
}
}
If you skipped the tldr section - the above steps are combined in this component and used in this example
3. Old, hacky try
Another way of doing this could be:
- receive a click on the a-frame entity
- grab the clicked coordinates like in 1
- hide the scene
- check out which
<area>
is at the coordinates withdocument.elementFromPoint(x, y);
. - show the scene
- create a mouse event with
document.createEvent("MouseEvent");
- dispatch it on the
<area>
element.
The hide / show trick works really good even on my mobile phone. I was really surprised that the scene wasn't flickering, freezing, even slowing down.
But document.elementFromPoint(x, y);
didn't work with firefox, and probably any attempt to make it work would be way more time consuming than the 0-2 steps. Also I believe the trappings would become bigger and case-dependant.
Anyway, here's the old-answer component:
/* SETUP
<a-scene>
<a-image press-map>
</a-scene>
<image id="image" sourcemap="map">
<map name="map">
<area ...>
</map>
*/
AFRAME.registerComponent("press-map", {
init: function() {
// the underlying image
this.img = document.querySelector("#image")
// react on clicks
this.el.addEventListener("click", evt => {
// get the point on the UV
let uvPoint = evt.detail.intersection.uv
// the y is inverted
let pointOnImage = {
x: uvPoint.x * this.img.width,
y: this.img.height - uvPoint.y * this.img.height
}
// the ugly show-hide bits
this.el.sceneEl.style.display = "none";
this.img.style.display = "block";
// !! grab the <area> at the (x,y) position
var el = document.elementFromPoint(pointOnImage.x, pointOnImage.y);
this.el.sceneEl.style.display="block"
this.img.style.display="none"
// create and dispatch the event
var ev = document.createEvent("MouseEvent");
ev.initMouseEvent(
"click",
true /* bubble */, false /* cancelable */,
window, null,
x, y, 0, 0, /* coordinates */
false, false, false, false, /* modifier keys */
0 /*left*/, null
);
el.dispatchEvent(ev);
}
}
})
HTML Image Maps - Is there an easy way to create and modify them?
Many program do this. It is to time consuming doing complicated maps manually.
I know coffeecup is a used tool for this.
Related Topics
Flipping/Inverting/Mirroring Text Using CSS Only
How to Apply a CSS Style to an Element Name
Replace Characters with HTML Entities in Java
Fire Event When Vimeo Video Stops Playing
Web Link to Specific Whatsapp Contact
CSS: How to Create a Gap Between Rows in a Table
HTML5 Drag and Drop Images from a Toolbar to a Canvas
Why Everything Word-Wrap Inside an Absolute Element Nested Inside a Float or Inline-Block Element
Responsive Square Divs Cross Browser Compatible
How to Highlight Source Code in HTML
CSS Background Image Not Displaying
Scale Svg to Container Without Mask/Crop
Trying to Align HTML Button at The Center of The My Page