I'm working on an application that uses Raphael to draw primitive shapes (rectangles, ellipses, triangles etc) and lines but allows the user to move/resize these objects as well. One of the main requirements is that the face of shapes can have formatted text. The actual text is a subset of Markdown (simple things like bolding, italics, lists) and is rendered as HTML.
FWIW - I'm using Backbone.js views to modularize the shape logic.
Approach 1
My initial thought was to use a combination of foreignObject
for SVG and direct HTML with VML for IE. However, IE9 doesn't support foreignObject
, and therefore this approach had to be abandoned.
Approach 2
With the beside the canvas object, add div
s that contain the actual HTML. Then, position them over the actual shape with a transparent background. I've created a shape view that has references to both the actual Raphael shape and the "overlay" div
. There are a couple of problems with this approach:
Using overlay that aren't children of the SVG/VML container feels wrong. Does having this overlay element cause other issues with rendering down the road?
Events that are normally trapped by Raphael (drag, click etc) need to be forwarded from the overlay to the Raphael object. For browsers that support
pointer-events
, this is easily done:div.shape-text-overlay { position: absolute; background: none; pointer-events: none; }
However, other browsers (like IE8 and below) need forwarding of the events:
var forwardedEvents = 'mousemove mousedown mouseup click dblclick mouseover mouseout'; this.$elText.on(forwardedEvents, function(e) { var original = e.originalEvent; var event; if (document.createEvent) { event = document.createEvent('HTMLEvents'); event.initEvent(e.type, true, true); } else { event = document.createEventObject(); event.eventType = e.type; } // FYI - This is the most simplistic approach to event forwarding. // My real implementation is much larger and uses MouseEvents and UIEvents separately. event.eventName = e.type; _.extend(event, original); if (document.createEvent) { that.el.node.dispatchEvent(event); } else { that.el.node.fireEvent('on' + event.eventType, event); } });
Overlapping shapes cause the text to be overlapped because the text/shapes are on different layers. Although overlapping shapes won't be common, this looks bad:
This approach is what I'm currently using but it just feels like a huge hack.
Approach 3
Almost like Approach 1, but this would involve writing text nodes/VML nodes directly. The biggest problem with this is the amount of manual conversion necessary. Breaking outside of Raphael's API seems like it could cause stability issues with the other Raphael objects.
Question
Has anyone else had to do something similar (rendering HTML inside of SVG/VML)? If so, how did you solve this problem? Were events taken into account?