Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

How To Make Complicated Multi Line Charts using Simple HTML and JavaScript

5.00/5 (3 votes)
19 Aug 2014CPOL 17.6K   114  
How to make complicated “multi line charts” using simple HTML and JavaScript

Introduction

Graphs on websites are an effective way of showing statistical data. And viewers can very easily find the trends.

Now displaying data through plug-ins like Flash and Silverlight can be a very expensive operation. The client needs to have Flash, Silverlight or any other plug-in, and the graph file will be downloaded in client browser that will slow down the process. And complicated code is required in addition to understanding the actual user requirements.

To overcome all the issues and achieving a custom developed graph, HTML and JavaScript enabled Graph is the easiest and most efficient solution.

To demonstrate this, I have taken a very complicated Graph that will have Temperature, Pulse, Respiratory and BP.

  • All vitals in one graph
  • Every vital with different defined ranges
  • Different color codes
  • Date wise and time wise
  • Lines in between the values
  • Values in the point with color codes
  • If the value is zero, it should not show

All the techniques implementation is completed using simple techniques.

Graph

Image 1

The above graph shows Temperature, Pulse, Respiratory and BP. All in one graph for multiple dates on different times

CSS

C#
<style type="text/css">

        #patientchart
        {
            width: 100%;
            height: 500px;
        }

        .charthead
        {
            position: fixed;
            background-color: #FFF;
            top: 180px;
            z-index: 100 !important;
        }

        .graphline
        {
            padding: 0px;
            margin: 0px;
            line-height: 1px;
            position: absolute;
            z-index: -1 !important;
            opacity: 0.5;
            filter: alpha(opacity=50);
        }

        .chart
        {
            border-left: 1px solid #DDD;
            border-top: 1px solid #DDD;
        }
        .chart td
        {
            font-size: 13px;
            border-right: 1px solid #DDD;
            border-bottom: 1px solid #DDD;
            text-align: center;
            width: 33px !important;
        }

        .pl
        {
            color: #1F7BC6;
        }
        .tl
        {
            color: #C61F1F;
        }
        .rl
        {
            color: #1FC64E;
        }
        .bp
        {
            color: #C69F1F;
        }

        .plh
        {
            background-color: #1F7BC6;
            color: #FFF;
            opacity: 0.8;
            filter: alpha(opacity=80);
        }
        .tlh
        {
            background-color: #C61F1F;
            color: #FFF;
            opacity: 0.8;
            filter: alpha(opacity=80);
        }
        .rlh
        {
            background-color: #1FC64E;
            color: #FFF;
            opacity: 0.8;
            filter: alpha(opacity=80);
        }
        .bph
        {
            background-color: #C69F1F;
            color: #FFF;
            opacity: 0.8;
            filter: alpha(opacity=80);
        }
        .sbph
        {
            background-color: #C69F1F;
            color: #000;
            opacity: 0.8;
            filter: alpha(opacity=80);
        }

        .c02
        {
            border-right-color: #DDD !important;
            border-bottom-color: #DDD !important;
        }
        .c06
        {
            border-right-color: #CCC !important;
            border-bottom-color: #CCC !important;
        }
        .c10
        {
            border-right-color: #BBB !important;
            border-bottom-color: #BBB !important;
        }
        .c14
        {
            border-right-color: #AAA !important;
            border-bottom-color: #AAA !important;
        }
        .c18
        {
            border-right-color: #999 !important;
            border-bottom-color: #999 !important;
        }
        .c22
        {
            border-right-color: #888 !important;
            border-bottom-color: #888 !important;
        }

        #dummyheader
        {
            height: 50px;
        }
    </style>

JavaScript

HTML
<script type="text/javascript">

	 // Main function to generating the graph

        function generateChartData() {
            generateReport();

     var listLength = VL.length;

	   // If List is empty do nothing

            if (listLength < 1) {
                return;
            }

	   // to hold the place for scrolling effect cancellation

            var ctbl = '<div id="dummyheader" />';

            ctbl += '<div id="detgr"></div><table class="chart charthead"
            cellspacing="0" id="chartheader"><tr><td colspan="4">DATE</td>';

            var firstDate = new Date(VL[0].date);

            var range = 4;
            var startDate = new Date(VL[listLength - 1].date);
            startDate.setDate(startDate.getDate() - range);
            startDate.setDate(startDate.getDate() + 1);

            if (firstDate > startDate) {
                startDate = firstDate;
            }

            var DATE = '';
            for (i = 0; i < range; i++) {
                var newDate = new Date(startDate);
                newDate.setDate(newDate.getDate() + i);
                DATE = GetFormatedDate(newDate);
                ctbl += '<td colspan="6">' + DATE + '</td>';
            }

            ctbl += '</tr>';
            ctbl += '<tr><td class="tlh">T</td><td class="plh">
            P</td><td class="rlh">R</td><td class="bph">B.P</td>';

	   // Hours
            var HRS = [];
            HRS.push('02');
            HRS.push('06');
            HRS.push('10');
            HRS.push('14');
            HRS.push('18');
            HRS.push('22');
            for (ti = 0; ti < range; ti++) {
                for (hi = 0; hi < HRS.length; hi++) {
                    ctbl += '<td class="c' + HRS[hi] + '">' + HRS[hi] + '</td>';
                }
            }
            ctbl += '</tr></table>';

            ctbl += '<div class="detdiv" id="detd"><table class="chart"
            style="z-index:-2 !important" cellspacing="0">'

            generateRanges();

            var RESVAL = '';
            var idx = 0;

            EPL = [];
            ETL = [];
            ERL = [];
            ESBP = [];
            EDBP = [];

            for (ri = 0; ri <= 45; ri++) {
                ctbl += '<tr><td class="tl">' + (TL[ri].val == 0 ? "" : TL[ri].val) +
                '</td><td class="pl">' + (PL[ri].val == 0 ? "" : PL[ri].val) +
                '</td><td class="rl">' + (RL[ri].val == 0 ? "" : RL[ri].val) +
                '</td><td class="bp">' + (BP[ri].val == 0 ? "" : BP[ri].val) + '</td>';

                for (ii = 0; ii < range; ii++) {
                    var newDate = new Date(startDate);
                    newDate.setDate(newDate.getDate() + ii);

                    for (ihi = 0; ihi < HRS.length; ihi++) {
                        RESVAL = '';
                        for (vi = 0; vi < listLength; vi++) {
                            if (GetFormatedDate(VL[vi].date) == GetFormatedDate(newDate)) {
                                var id = new Date(VL[vi].date);
                                if (id.getHours() == (HRS[ihi] * 1)) {
                                    RESVAL = getVTL(vi, idx);
                                }
                            }
                        }
                        ctbl += '<td class="c' + HRS[ihi] + '">' + RESVAL + '</td>';
                    }
                }
                ctbl += '</tr>';
                idx++;
            }

            ctbl += '</table></div>';
            $('#patientchart').html(ctbl);
            drawGraphs();

	   // to show the header always on top

            $(window).scroll(function () {
                var head = $(window).scrollTop();
                if (head > 180) {
                    $('#chartheader').css('top', '0px');
                } else {
                    head = 175 - head;
                    $('#chartheader').css('top', head + 'px');
                }
            });
        }

// Functions for getting date formats, that handles pretty much every format

        function GetFormatedDate(date) {
            var dt = new Date(date);
            return (dt.getMonth() + 1) + '/' + dt.getDate() + '/' + dt.getFullYear();
        }
        function GetFormatedDateTime(date) {
            var dt = new Date(date);
            return (dt.getMonth() + 1) + '/' + dt.getDate() + '/' + dt.getFullYear() + ' ' + dt.getHours();
        }

// Variables for saving values

        var VL = [];

        var PL = [];
        var TL = [];
        var RL = [];
        var BP = [];

        function generateReport() {

	// List of data can be from database or hidden field

        var list = $('#HF_Vitals').val().split('||');
            VL = [];
            if (list.length < 1) {
                return;
            }

            for (vi = 0; vi < list.length; vi++) {
                var row = list[vi].split('|');

                VL.push({ date: row[0],
                    dbp: Math.round(row[1]),
                    pul: Math.round(row[2]),
                    res: Math.round(row[3]),
                    sbp: Math.round(row[4]),
                    temp: row[5] * 1,
                    counter: Math.round(row[6])
                });
            }
        }

	// here we define the ranges for graph (note these should be defined to view graph in one layout)

        function generateRanges() {
            PL = [];
            TL = [];
            RL = [];
            BP = [];

            PL.push({ ri: 0, val: 0 });
            PL.push({ ri: 1, val: 0 });
            PL.push({ ri: 2, val: 0 });
            PL.push({ ri: 3, val: 150 });
            PL.push({ ri: 4, val: 149 });
            PL.push({ ri: 5, val: 148 });
            PL.push({ ri: 6, val: 145 });
            PL.push({ ri: 7, val: 142 });
            PL.push({ ri: 8, val: 136 });
            PL.push({ ri: 9, val: 133 });
            PL.push({ ri: 10, val: 130 });
            PL.push({ ri: 11, val: 127 });
            PL.push({ ri: 12, val: 124 });
            PL.push({ ri: 13, val: 121 });
            PL.push({ ri: 14, val: 118 });
            PL.push({ ri: 15, val: 115 });
            PL.push({ ri: 16, val: 112 });
            PL.push({ ri: 17, val: 109 });
            PL.push({ ri: 18, val: 106 });
            PL.push({ ri: 19, val: 103 });
            PL.push({ ri: 20, val: 100 });
            PL.push({ ri: 21, val: 97 });
            PL.push({ ri: 22, val: 94 });
            PL.push({ ri: 23, val: 91 });
            PL.push({ ri: 24, val: 88 });
            PL.push({ ri: 25, val: 85 });
            PL.push({ ri: 26, val: 82 });
            PL.push({ ri: 27, val: 79 });
            PL.push({ ri: 28, val: 76 });
            PL.push({ ri: 29, val: 73 });
            PL.push({ ri: 30, val: 70 });
            PL.push({ ri: 31, val: 67 });
            PL.push({ ri: 32, val: 64 });
            PL.push({ ri: 33, val: 61 });
            PL.push({ ri: 34, val: 58 });
            PL.push({ ri: 35, val: 55 });
            PL.push({ ri: 36, val: 49 });
            PL.push({ ri: 37, val: 46 });
            PL.push({ ri: 38, val: 43 });
            PL.push({ ri: 39, val: 40 });
            PL.push({ ri: 40, val: 0 });
            PL.push({ ri: 41, val: 0 });
            PL.push({ ri: 42, val: 0 });
            PL.push({ ri: 43, val: 0 });
            PL.push({ ri: 44, val: 0 });
            PL.push({ ri: 45, val: 0 });

            TL.push({ ri: 0, val: 0 });
            TL.push({ ri: 1, val: 0 });
            TL.push({ ri: 2, val: 0 });
            TL.push({ ri: 3, val: 0 });
            TL.push({ ri: 4, val: 0 });
            TL.push({ ri: 5, val: 0 });
            TL.push({ ri: 6, val: 0 });
            TL.push({ ri: 7, val: 0 });
            TL.push({ ri: 8, val: 0 });
            TL.push({ ri: 9, val: 0 });
            TL.push({ ri: 10, val: 0 });
            TL.push({ ri: 11, val: 0 });
            TL.push({ ri: 12, val: 0 });
            TL.push({ ri: 13, val: 0 });
            TL.push({ ri: 14, val: 0 });
            TL.push({ ri: 15, val: 0 });
            TL.push({ ri: 16, val: 0 });
            TL.push({ ri: 17, val: 0 });
            TL.push({ ri: 18, val: 0 });
            TL.push({ ri: 19, val: 0 });
            TL.push({ ri: 20, val: 0 });
            TL.push({ ri: 21, val: 0 });
            TL.push({ ri: 22, val: 0 });
            TL.push({ ri: 23, val: 0 });
            TL.push({ ri: 24, val: 0 });
            TL.push({ ri: 25, val: 0 });
            TL.push({ ri: 26, val: 0 });
            TL.push({ ri: 27, val: 105.0 });
            TL.push({ ri: 28, val: 104.5 });
            TL.push({ ri: 29, val: 104.0 });
            TL.push({ ri: 30, val: 103.5 });
            TL.push({ ri: 31, val: 103.0 });
            TL.push({ ri: 32, val: 102.5 });
            TL.push({ ri: 33, val: 102.0 });
            TL.push({ ri: 34, val: 101.5 });
            TL.push({ ri: 35, val: 101.0 });
            TL.push({ ri: 36, val: 100.5 });
            TL.push({ ri: 37, val: 100.0 });
            TL.push({ ri: 38, val: 99.5 });
            TL.push({ ri: 39, val: 99.0 });
            TL.push({ ri: 40, val: 98.5 });
            TL.push({ ri: 41, val: 98.0 });
            TL.push({ ri: 42, val: 97.5 });
            TL.push({ ri: 43, val: 97.0 });
            TL.push({ ri: 44, val: 96.5 });
            TL.push({ ri: 45, val: 96.0 });

            RL.push({ ri: 0, val: 40 });
            RL.push({ ri: 1, val: 38 });
            RL.push({ ri: 2, val: 36 });
            RL.push({ ri: 3, val: 34 });
            RL.push({ ri: 4, val: 32 });
            RL.push({ ri: 5, val: 30 });
            RL.push({ ri: 6, val: 28 });
            RL.push({ ri: 7, val: 26 });
            RL.push({ ri: 8, val: 24 });
            RL.push({ ri: 9, val: 22 });
            RL.push({ ri: 10, val: 20 });
            RL.push({ ri: 11, val: 18 });
            RL.push({ ri: 12, val: 16 });
            RL.push({ ri: 13, val: 14 });
            RL.push({ ri: 14, val: 12 });
            RL.push({ ri: 15, val: 0 });
            RL.push({ ri: 16, val: 0 });
            RL.push({ ri: 17, val: 0 });
            RL.push({ ri: 18, val: 0 });
            RL.push({ ri: 19, val: 0 });
            RL.push({ ri: 20, val: 0 });
            RL.push({ ri: 21, val: 0 });
            RL.push({ ri: 22, val: 0 });
            RL.push({ ri: 23, val: 0 });
            RL.push({ ri: 24, val: 0 });
            RL.push({ ri: 25, val: 0 });
            RL.push({ ri: 26, val: 0 });
            RL.push({ ri: 27, val: 0 });
            RL.push({ ri: 28, val: 0 });
            RL.push({ ri: 29, val: 0 });
            RL.push({ ri: 30, val: 0 });
            RL.push({ ri: 31, val: 0 });
            RL.push({ ri: 32, val: 0 });
            RL.push({ ri: 33, val: 0 });
            RL.push({ ri: 34, val: 0 });
            RL.push({ ri: 35, val: 0 });
            RL.push({ ri: 36, val: 0 });
            RL.push({ ri: 37, val: 0 });
            RL.push({ ri: 38, val: 0 });
            RL.push({ ri: 39, val: 0 });
            RL.push({ ri: 40, val: 0 });
            RL.push({ ri: 41, val: 0 });
            RL.push({ ri: 42, val: 0 });
            RL.push({ ri: 43, val: 0 });
            RL.push({ ri: 44, val: 0 });
            RL.push({ ri: 45, val: 0 });

            BP.push({ ri: 0, val: 250 });
            BP.push({ ri: 1, val: 240 });
            BP.push({ ri: 2, val: 230 });
            BP.push({ ri: 3, val: 220 });
            BP.push({ ri: 4, val: 210 });
            BP.push({ ri: 5, val: 200 });
            BP.push({ ri: 6, val: 190 });
            BP.push({ ri: 7, val: 180 });
            BP.push({ ri: 8, val: 170 });
            BP.push({ ri: 9, val: 160 });
            BP.push({ ri: 10, val: 150 });
            BP.push({ ri: 11, val: 140 });
            BP.push({ ri: 12, val: 130 });
            BP.push({ ri: 13, val: 120 });
            BP.push({ ri: 14, val: 110 });
            BP.push({ ri: 15, val: 100 });
            BP.push({ ri: 16, val: 90 });
            BP.push({ ri: 17, val: 80 });
            BP.push({ ri: 18, val: 70 });
            BP.push({ ri: 19, val: 60 });
            BP.push({ ri: 20, val: 50 });
            BP.push({ ri: 21, val: 40 });
            BP.push({ ri: 22, val: 30 });
            BP.push({ ri: 23, val: 20 });
            BP.push({ ri: 24, val: 0 });
            BP.push({ ri: 25, val: 0 });
            BP.push({ ri: 26, val: 0 });
            BP.push({ ri: 27, val: 0 });
            BP.push({ ri: 28, val: 0 });
            BP.push({ ri: 29, val: 0 });
            BP.push({ ri: 30, val: 0 });
            BP.push({ ri: 31, val: 0 });
            BP.push({ ri: 32, val: 0 });
            BP.push({ ri: 33, val: 0 });
            BP.push({ ri: 34, val: 0 });
            BP.push({ ri: 35, val: 0 });
            BP.push({ ri: 36, val: 0 });
            BP.push({ ri: 37, val: 0 });
            BP.push({ ri: 38, val: 0 });
            BP.push({ ri: 39, val: 0 });
            BP.push({ ri: 40, val: 0 });
            BP.push({ ri: 41, val: 0 });
            BP.push({ ri: 42, val: 0 });
            BP.push({ ri: 43, val: 0 });
            BP.push({ ri: 44, val: 0 });
            BP.push({ ri: 45, val: 0 });
        }

	// arrays to be used for lines

        var EPL = [];
        var ETL = [];
        var ERL = [];
        var ESBP = [];
        var EDBP = [];

	// add points in graph and put results in arrays (so that lines can be drawn between them)

        function getVTL(vi, idx) {

            var resval = '';
            var vt = VL[vi];
            var did = vt.counter;
            if (vt.dbp > 0) {
                if (BP[idx].val == VL[vi].dbp) {
                    resval += '<div class="bph" id="bp_' + did + '">' + (vt.dbp * 1) + '</div>';
                    EDBP.push(did);
                }
            }

            if (vt.pul > 0) {
                if (PL[idx].val == vt.pul) {
                    resval += '<div class="plh" id="pul_' + did + '">' + vt.pul + '</div>';
                    EPL.push(did);
                }
            }

            if (VL[vi].res > 0) {
                if (RL[idx].val == vt.res) {
                    resval += '<div class="rlh" id="res_' + did + '">' + vt.res + '</div>';
                    ERL.push(did);
                }
            }

            if (vt.sbp > 0) {
                if (BP[idx].val == vt.sbp) {
                    resval += '<div class="sbph" id="sbp_' + did + '">' + (vt.sbp * 1) + '</div>';
                    ESBP.push(did);
                }
            }

            if (vt.temp > 0) {
                if (TL[idx].val == vt.temp) {
                    resval += '<div class="tlh" id="temp_' + did + '">' + vt.temp + '</div>';
                    ETL.push(did);
                }
            }

            return resval;
        }

	// sort number is used to change nature or sorting for array

        function sortNumber(a, b) {
            return a - b;
        }

        function drawGraphs() {
            var div1 = '';
            var div2 = '';
            var idx = 0;
            var str = '';

	   // Sort Arrays in numeric nature

            EDBP.sort(sortNumber);
            EPL.sort(sortNumber);
            ERL.sort(sortNumber);
            ESBP.sort(sortNumber);
            ETL.sort(sortNumber);

            for (i = 0; i <= EDBP.length + 1; i++) {
                str += EDBP[i] + ',';
                div1 = 'bp_' + EDBP[i];
                div2 = 'bp_' + EDBP[i + 1];
                connect(div1, div2, '#c69f1f', 2);
            }
            for (i = 0; i <= EPL.length + 1; i++) {
                div1 = 'pul_' + EPL[i];
                div2 = 'pul_' + EPL[i + 1];
                connect(div1, div2, '#1f7bc6', 2);
            }

            for (i = 0; i <= ERL.length + 1; i++) {
                div1 = 'res_' + ERL[i];
                div2 = 'res_' + ERL[i + 1];
                connect(div1, div2, '#1fc64e', 2);
            }

            for (i = 0; i <= ESBP.length + 1; i++) {
                div1 = 'sbp_' + ESBP[i];
                div2 = 'sbp_' + ESBP[i + 1];
                connect(div1, div2, '#c69f1f', 2);
            }

            for (i = 0; i < ETL.length + 1; i++) {
                div1 = 'temp_' + ETL[i];
                div2 = 'temp_' + ETL[i + 1];
                connect(div1, div2, '#c61f1f', 2);
            }
        }

	// get the off sets of results

        function getOffset(el) {
            if (el == null) {
                return;
            }
            var _x = 0;
            var _y = 0;
            var _w = el.offsetWidth | 0;
            var _h = el.offsetHeight | 0;
            while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
                _x += el.offsetLeft - el.scrollLeft;
                _y += el.offsetTop - el.scrollTop;
                el = el.offsetParent;
            }
            return { top: _y, left: _x, width: _w, height: _h };
        }

	// connect two nearest points
	// make a div and rotate it between two points (center of point)

        function connect(d1, d2, color, thickness) {
            var div1 = document.getElementById(d1);
            var div2 = document.getElementById(d2);
            if (div1 == null || div2 == null) {
                return;
            }
            var off1 = getOffset(div1);
            var off2 = getOffset(div2);
            // bottom right
            var x1 = off1.left + (off1.width / 2);
            var y1 = off1.top + (off1.height / 2);
            // top right
            var x2 = off2.left + (off2.width / 2); //  + off2.width;
            var y2 = off2.top + (off2.height / 2);
            // distance
            var length = Math.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
            // center
            var cx = ((x1 + x2) / 2) - (length / 2);
            var cy = ((y1 + y2) / 2) - (thickness / 2);
            // angle
            var angle = Math.atan2((y1 - y2), (x1 - x2)) * (180 / Math.PI);
            // make hr
            var htmlLine = "<div class='graphline' style=' height:" +
            thickness + "px; background-color:" + color + ";  left:" + cx +
            "px; top:" + cy + "px; width:" + length + "px; -moz-transform:rotate
            (" + angle + "deg); -webkit-transform:rotate(" + angle + "deg);
            -o-transform:rotate(" + angle + "deg);
            -ms-transform:rotate(" + angle + "deg); transform:rotate(" + angle + "deg);' />";
            //
            //alert(htmlLine);
            document.body.innerHTML += htmlLine;
        }

    </script>

HTML Code

HTML
<input name="HF_Vitals" id="HF_Vitals"
value="Saturday, August 16, 2014 6:00:00 PM | 100.00| 73.00| 18.00|  170.00| 98.50| 0||Sunday, August 17, 2014 10:00:00 AM|90.00|115.00|0|160.00|98.50|1||Sunday, August 17, 2014 6:00:00 PM|80.00|76.00|18.00|120.00|98.50|2||Monday, August 18, 2014 10:00:00 AM|110.00|115.00|0|170.00|98.50|3||Monday, August 18, 2014 6:00:00 PM|70.00|67.00|0|130.00|100.00|4"
type="hidden" />

<input type="button" id="btn_showgraph"
value="Show Graph" onclick="generateChartData()" />

<div id="patientchart"></div>

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)