Skip to main content
2 of 8
Included code
Adrian
  • 151
  • 5

Is this a good way of writing custom tag attributes and rendering code?

I've not done javascript in quite a while, but I wanted to make it so that I can circle (in many different ways) around the bounding box of a tagged item.

Is this a good way of doing what I'm wanting? Is there a better way? I.e. can this be done with only css?

http://jsfiddle.net/adrianh/4SVHH/27/

// Creating a tag with the class 'circled' will have that element circled
// using a number of different ways by setting data-style attribute to:
//   'default'  - an open ellipse (default)
//   'ellipse'  - a closed ellipse
//   'circle'   - a closed circle circumscribed around the bounding box.
//   'rect'     - a closed rectangle
//   'vbracket' - a square bracket like [ ]
//   'hbracket' - a square bracket that is rotated 90 degrees
//
// Other attributes are:
//   data-width - width of lines used to circle element. (default='2')
//   data-style - can be: 'solid', 'dotted' or 'dashed'. (default='solid')
//   data-color - any html color. (default='red')
//
// Note: The circle is around the bounding box of the tagged object,
//       so if the object wraps, it will span the paragraph width.
//
// Note: Circle will be redrawn if window is resized.

(function()
{
    // draws an ellipse
    function drawEllipse(context, centerX, centerY, width, height)
    {
        var ratio = height/width;
        context.save();
        context.scale(1,  ratio);
        context.beginPath();
        context.arc(centerX, centerY/ratio, width/2, 0, 2 * Math.PI);
        context.restore();
    };

    // returns the appropriate array for the style requested
    function getLineDash(style)
    {
        if (style == "solid")
            return [];
        if (style == 'dotted')
            return [3];
        if (style == 'dashed')
            return [6, 3];
        return [];
    };
    
    // points at object that contains all the circle canvases
    var circles;
    
    // Return a canvas given an indexed number.  If the same
    // number is used multiple times, then that canvas is reused.
    // Canvas is positioned in the (left, top) coordinate in the
    // document with the specified width and height.
    function getCanvas(i, left, top, width, height)
    {
        var canvas;
        if (typeof circles.childNodes[i] != 'undefined')
        {
            canvas = circles.childNodes[i];
            canvas.width=width;
            canvas.height=height;
            canvas.style.left=left+"px";
            canvas.style.top=top+"px";
        }
        else
        {
            canvas = "<canvas "+
            "width='"+width+"' "+
            "height='"+height+"' "+
            "style='"+
                "position:absolute;"+
                "z-index:0;"+
                "left:"+left+"px;"+
                "top:"+top+"px;"+
                "pointer-events:none;"+
                //"border:1px solid;"+
            "' />";
            circles.insertAdjacentHTML('beforeend', canvas);
        }
        return circles.childNodes[i];
    };
    
    // draw a circle circumscribed around the rect
    function circleRect(i, rect, lineWidth, lineColor, lineStyle)
    {
        var diameter = Math.sqrt(rect.width*rect.width+rect.height*rect.height);
        var cx = (rect.right + rect.left)/2;
        var cy = (rect.top + rect.bottom)/2;
        var left = Math.floor(cx - diameter/2);
        var top  = Math.floor(cy - diameter/2);
        diameter = Math.floor(diameter);
        var canvas = getCanvas(i, left-1, top-1, diameter+2, diameter+2);
        var ctx=canvas.getContext("2d");
        ctx.beginPath();
        ctx.arc(diameter/2+1,diameter/2+1,diameter/2,0,2*Math.PI);
        ctx.lineWidth   = lineWidth;
        ctx.strokeStyle = lineColor;
        ctx.setLineDash = getLineDash(lineStyle);
        ctx.stroke();
    };
    
    // draw an open ellipse around the rect.
    function defaultRect(i, rect, lineWidth, lineColor, lineStyle)
    {
        var quarterWidth = rect.width/4;
        var quarterHeight = rect.height/4;
        var eighthWidth = quarterWidth/2;
        var eighthHeight = quarterHeight/2;
        var padx = quarterWidth;
        var pady = quarterHeight*1.5;
        var width = rect.width;
        var height = rect.height;
        var canvas = getCanvas(i, rect.left-padx, rect.top-pady, width+padx*2, height+pady*2);
        //canvas.style.border="1px solid yellow";
        var ctx=canvas.getContext("2d");
        ctx.beginPath();
        ctx.moveTo(       padx+width/2,             pady+eighthHeight);
        ctx.bezierCurveTo(padx-quarterWidth,        pady+quarterHeight/3,
                          padx,                     pady+height+eighthHeight,
                          padx+width/2-eighthWidth, pady+height+eighthHeight);
        ctx.bezierCurveTo(padx+width+quarterWidth,  pady+height+quarterHeight,
                          padx+width+eighthWidth,   pady-eighthHeight*3,
                          padx+width/2-eighthWidth, pady-quarterHeight);
        ctx.lineWidth   = lineWidth;
        ctx.strokeStyle = lineColor;
        ctx.setLineDash(getLineDash(lineStyle));
        ctx.stroke();
    //    ctx.strokeStyle = "blue";
    //    ctx.lineWidth = 1;
    //    ctx.strokeRect(padx, pady, width, height);
    };
    
    // draw an ellipse around the rect
    function ellipseRect(i, rect, lineWidth, lineColor, lineStyle)
    {
        var padx = 200;
        var pady = 200;
        var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2);
        var ctx=canvas.getContext("2d");
        //canvas.style.border = "1px blue solid"
        drawEllipse(ctx, rect.width/2+padx, rect.height/2+pady, rect.width, rect.height);
        ctx.lineWidth   = lineWidth;
        ctx.strokeStyle = lineColor;
        ctx.setLineDash(getLineDash(lineStyle));
        ctx.stroke();
    };
    
    // Draw a rectangle around the rect.
    function rectRect(i, rect, lineWidth, lineColor, lineStyle)
    {
        var half = Math.ceil(lineWidth/2);
        var padx = lineWidth;
        var pady = lineWidth;
        var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2);
        //canvas.style.border = "1px blue solid";
        var ctx=canvas.getContext("2d");
        ctx.lineWidth   = lineWidth;
        ctx.strokeStyle = lineColor;
        ctx.setLineDash(getLineDash(lineStyle));
        ctx.strokeRect(padx-half, pady-half, rect.width+lineWidth/2, rect.height+lineWidth/2);
    };
    
    // Draw a rectangle around the rect.
    function vbracketRect(i, rect, lineWidth, lineColor, lineStyle)
    {
        var half = Math.ceil(lineWidth/2);
        var padx = lineWidth*2;
        var pady = lineWidth*2;
        var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2);
        //canvas.style.border = "1px blue solid";
        var ctx=canvas.getContext("2d");
        ctx.lineWidth   = lineWidth;
        ctx.strokeStyle = lineColor;
        ctx.setLineDash(getLineDash(lineStyle));
        ctx.beginPath();
        ctx.moveTo(padx-half+half*3            , pady-half);
        ctx.lineTo(padx-half                   , pady-half);
        ctx.lineTo(padx-half                   , pady+half+rect.height);
        ctx.lineTo(padx-half+half*3            , pady+half+rect.height);
        ctx.moveTo(padx+half+rect.width-half*3 , pady-half);
        ctx.lineTo(padx+half+rect.width        , pady-half);
        ctx.lineTo(padx+half+rect.width        , pady+half+rect.height);
        ctx.lineTo(padx+half+rect.width-half*3 , pady+half+rect.height);
        ctx.stroke();
    };
    
    function hbracketRect(i, rect, lineWidth, lineColor, lineStyle)
    {
        var half = Math.ceil(lineWidth/2);
        var padx = lineWidth*2;
        var pady = lineWidth*2;
        var canvas = getCanvas(i, rect.left-padx, rect.top-pady, rect.width+padx*2, rect.height+pady*2);
        //canvas.style.border = "1px blue solid";
        var ctx=canvas.getContext("2d");
        ctx.lineWidth   = lineWidth;
        ctx.strokeStyle = lineColor;
        ctx.setLineDash(getLineDash(lineStyle));
        ctx.beginPath();
        ctx.moveTo(padx-half                   , pady-half+half*3);
        ctx.lineTo(padx-half                   , pady-half);
        ctx.lineTo(padx+half+rect.width        , pady-half);
        ctx.lineTo(padx+half+rect.width        , pady-half+half*3);
        ctx.moveTo(padx-half                   , pady+half+rect.height-half*3);
        ctx.lineTo(padx-half                   , pady+half+rect.height);
        ctx.lineTo(padx+half+rect.width        , pady+half+rect.height);
        ctx.lineTo(padx+half+rect.width        , pady+half+rect.height-half*3);
        ctx.stroke();
    };
    
    // if x is null return _default, otherwise return x.
    function val(x, _default)
    {
        return (x == null) ? _default : x;
    };

    // This draws the circles around all 'circled' class objects.
    function drawCircles()
    {
        $(".circled").each(function(i, obj) {
            var rect  = obj.getBoundingClientRect();
            var type  = val(obj.getAttribute("data-type"), "default");
            var width = val(obj.getAttribute("data-width"), 2);
            var color = val(obj.getAttribute("data-color"), "red");
            var style = val(obj.getAttribute("data-style"), "solid");
            if (type == 'default')
            {
                defaultRect(i, rect, width, color, style);
            }
            else if (type == 'rect')
            {
                rectRect(i, rect, width, color, style);
            }
            else if (type == 'ellipse')
            {
                ellipseRect(i, rect, width, color, style);
            }
            else if (type == 'vbracket')
            {
                vbracketRect(i, rect, width, color, style);
            }
            else if (type == 'hbracket')
            {
                hbracketRect(i, rect, width, color, style);
            }
            else if (type == 'circle')
            {
                circleRect(i, rect, width, color, style);
            }
        });
    };

    // Place a div container to place all of the canvases in.
    document.body.insertAdjacentHTML('afterbegin', '<div id="circles"></div>');
    circles = document.getElementById("circles");
    
    // Draw the circles upon loading the document and 
    // if the window extents have changed.
    window.onload=drawCircles;
    window.onresize=drawCircles;
})();
Adrian
  • 151
  • 5