Skip to content

Instantly share code, notes, and snippets.

@wendlingd
Last active April 20, 2019 13:21
Show Gist options
  • Save wendlingd/af1e751e97c5211ff11277c985e5e642 to your computer and use it in GitHub Desktop.
Save wendlingd/af1e751e97c5211ff11277c985e5e642 to your computer and use it in GitHub Desktop.
Interactive treemap with shading
border: no
height: 750
license: gpl-3.0

README.md

D3.js web site health check - Treemap, shaded, from two-level JSON

Source: Nathan Yau's Jobs Charted by State and Salary.

Whole-ecology treemap visualization to surface quality problems in a large web site. Foundational data is content inventory plus traffic. Can be part of a rolling audit, updated perhaps monthly with a spidering tool and a traffic export/import. Then you pair the foundation with one or more new error reports of the moment, such as broken links. Helps product managers comprehend what their highest-severity problems are, and helps them decide what communication packages (microsites, thematic groups of pages...) should be fixed first.

Traffic can tell PART of the story, but is not definitive - so product managers can disclose the traffic sequentially and decide how much weight to give it, based on the numbers a particular communication package was meant to reach relative to others in their care. While traffic should certainly play a role in how you manage your site, you should NOT use traffic data in a way that damages your relationship with small target audiences. All site traffic should be judged relative to the size of their target audiences.

Here, points are used instead of item counts, so management is able to shift work priorities by raising the severity of one or more error types. Production version includes a leaderboard.

The database this runs with has multiple error / fix reports, including:

  • Broken links
  • Outdated video formats
  • Content not reviewed by a subject specialist in the past 12 months
  • Pages linking to Word, Excel, and other "non-web" documents that should be evaluated for conversion to HTML

Create a sortable HTML table below as an accessible equivalent. Current colors are great for people who don't have color blindness; you may need to change the shading for your audience.

Verbose description, advice: Interactive treemap with shading.

{
"name": "contentByGroup",
"children":[
{
"pageGroupID": "30000",
"name": "Nursing topics",
"children": [
{
"pageGroupID": "4692",
"name": "A manual of nursing",
"groupPageCount": 111,
"aveMonthlyViewsGr": 49169,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4182",
"name": "A century of nursing",
"groupPageCount": 426,
"aveMonthlyViewsGr": 103375,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4412",
"name": "Manual of rules for child-bed nursing",
"groupPageCount": 57,
"aveMonthlyViewsGr": 518,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4662",
"name": "Hospital sisters and their duties",
"groupPageCount": 12,
"aveMonthlyViewsGr": 0,
"repairTotCount": 10,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4195",
"name": "Fever-nursing",
"groupPageCount": 161,
"aveMonthlyViewsGr": 46,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4667",
"name": "The nurse, or, Hints on the care of the sick",
"groupPageCount": 53,
"aveMonthlyViewsGr": 848,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4674",
"name": "The historical development of modern nursing",
"groupPageCount": 146,
"aveMonthlyViewsGr": 6303,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4668",
"name": "Remarks on the training of nurses",
"groupPageCount": 137,
"aveMonthlyViewsGr": 685,
"repairTotCount": 10,
"url": "to-do_list.html"
}
]
}
,{
"pageGroupID": "20000",
"name": "Hospital topics",
"children": [
{
"pageGroupID": "4255",
"name": "Methodist General Hospital: Origin, purposes, needs",
"groupPageCount": 146,
"aveMonthlyViewsGr": 5941,
"repairTotCount": 2,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4253",
"name": "Laws and regulations for the admission and discharge of patients",
"groupPageCount": 8,
"aveMonthlyViewsGr": 11605,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4257",
"name": "Experiments and observations on the summer ventilation and cooling of hospitals",
"groupPageCount": 7,
"aveMonthlyViewsGr": 11358,
"repairTotCount": 0,
"url": "to-do_list.html"
}
]
}
,{
"pageGroupID": "50000",
"name": "Physician topics",
"children": [
{
"pageGroupID": "4308",
"name": "Two lectures on the conduct of the medical life",
"groupPageCount": 243,
"aveMonthlyViewsGr": 914,
"repairTotCount": 23,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4285",
"name": "Memoir of Butler Wilmarth, M.D.",
"groupPageCount": 55,
"aveMonthlyViewsGr": 10,
"repairTotCount": 84,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4270",
"name": "The ups and downs of a doctor's life",
"groupPageCount": 164,
"aveMonthlyViewsGr": 33154,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4288",
"name": "Addresses on the reciprocal relations existing between physicians and the public",
"groupPageCount": 10,
"aveMonthlyViewsGr": 41224,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4276",
"name": "Proofs of the origin of the yellow fever",
"groupPageCount": 102,
"aveMonthlyViewsGr": 1235,
"repairTotCount": 26,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4432",
"name": "The relation of the medical profession to science",
"groupPageCount": 230,
"aveMonthlyViewsGr": 56028,
"repairTotCount": 68,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4316",
"name": "The physician's knowledge: Orations",
"groupPageCount": 23,
"aveMonthlyViewsGr": 22373,
"repairTotCount": 52,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4313",
"name": "Memoir of the life and character of Prof. Valentine Mott",
"groupPageCount": 52,
"aveMonthlyViewsGr": 2921,
"repairTotCount": 2,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4344",
"name": "On the responsibilities of the medical profession",
"groupPageCount": 350,
"aveMonthlyViewsGr": 77068,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4382",
"name": "The reciprocal relations of physicians and clergymen",
"groupPageCount": 151,
"aveMonthlyViewsGr": 1366,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4317",
"name": "Addresses to Medical Societies",
"groupPageCount": 145,
"aveMonthlyViewsGr": 13366,
"repairTotCount": 2,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4278",
"name": "The elements of medical eminence",
"groupPageCount": 141,
"aveMonthlyViewsGr": 74203,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4299",
"name": "The difficulties, duties, and rewards of the physician",
"groupPageCount": 119,
"aveMonthlyViewsGr": 61265,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4376",
"name": "The country physician",
"groupPageCount": 118,
"aveMonthlyViewsGr": 11548,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4361",
"name": "Benjamin Rush and American psychiatry",
"groupPageCount": 113,
"aveMonthlyViewsGr": 2819,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4346",
"name": "A sketch of the life of Horatio R. Storer, M.D.",
"groupPageCount": 113,
"aveMonthlyViewsGr": 2819,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4284",
"name": "The century's progress in anatomy and physiology",
"groupPageCount": 109,
"aveMonthlyViewsGr": 10869,
"repairTotCount": 2,
"url": "to-do_list.html"
}
]
}
,{
"pageGroupID": "60000",
"name": "Surgery topics",
"children": [
{
"pageGroupID": "4419",
"name": "The disinfection of dental and surgical instruments",
"groupPageCount": 197,
"aveMonthlyViewsGr": 4318,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4418",
"name": "A new handle and grip for scissors for plastic and other delicate work",
"groupPageCount": 195,
"aveMonthlyViewsGr": 1282,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4421",
"name": "Catalogue of surgical instruments and physicians' supplies, electric and orthopedic apparatus",
"groupPageCount": 195,
"aveMonthlyViewsGr": 67329,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4420",
"name": "Pompeian surgery and surgical instruments",
"groupPageCount": 194,
"aveMonthlyViewsGr": 1372,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4413",
"name": "The principles and practice of surgery",
"groupPageCount": 159,
"aveMonthlyViewsGr": 196426,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4422",
"name": "General surgical pathology and therapeutics",
"groupPageCount": 156,
"aveMonthlyViewsGr": 60598,
"repairTotCount": 5,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4416",
"name": "Manual of operative surgery",
"groupPageCount": 150,
"aveMonthlyViewsGr": 30321,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4417",
"name": "Minor surgery and bandaging",
"groupPageCount": 150,
"aveMonthlyViewsGr": 47197,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4425",
"name": "The present status of abdominal surgery",
"groupPageCount": 108,
"aveMonthlyViewsGr": 12054,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4423",
"name": "On the antiseptic treatment of wounds",
"groupPageCount": 42,
"aveMonthlyViewsGr": 57917,
"repairTotCount": 0,
"url": "to-do_list.html"
}
]
}
,{
"pageGroupID": "70000",
"name": "Military topics",
"children": [
{
"pageGroupID": "4464",
"name": "The Medical Department, U.S. Army",
"groupPageCount": 198,
"aveMonthlyViewsGr": 7309,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4459",
"name": "On the duties of surgeons on the march",
"groupPageCount": 212,
"aveMonthlyViewsGr": 155,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4461",
"name": "Reports on the condition of military hospitals",
"groupPageCount": 199,
"aveMonthlyViewsGr": 1000,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4460",
"name": "Instructions for medical inspectors of the United States Army",
"groupPageCount": 349,
"aveMonthlyViewsGr": 689,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4442",
"name": "Rules regarding the measures to be adopted on the outbreak of cholera or appearance of small-pox",
"groupPageCount": 103,
"aveMonthlyViewsGr": 16029,
"repairTotCount": 6,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4454",
"name": "A manual of military surgery",
"groupPageCount": 67,
"aveMonthlyViewsGr": 231,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4449",
"name": "Rules for preserving the health of the soldier",
"groupPageCount": 20,
"aveMonthlyViewsGr": 2464,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4437",
"name": "Hints for the control and prevention of infectious diseases, in camps, transports, and hospitals",
"groupPageCount": 19,
"aveMonthlyViewsGr": 1493,
"repairTotCount": 2,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4575",
"name": "Regulations for the Medical Department of the Army",
"groupPageCount": 19,
"aveMonthlyViewsGr": 1493,
"repairTotCount": 2,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4324",
"name": "Directions to Army surgeons on the field of battle",
"groupPageCount": 17,
"aveMonthlyViewsGr": 14864,
"repairTotCount": 8,
"url": "to-do_list.html"
}
]
}
,{
"pageGroupID": "160000",
"name": "Medical topics from before 1900",
"children": [
{
"pageGroupID": "4436",
"name": "Lasting legacies: Gifts to medicine and the American people",
"groupPageCount": 67,
"aveMonthlyViewsGr": 4,
"repairTotCount": 2,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4494",
"name": "The new forensic science",
"groupPageCount": 42,
"aveMonthlyViewsGr": 29584,
"repairTotCount": 2,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4170",
"name": "The old root and herb doctor",
"groupPageCount": 32,
"aveMonthlyViewsGr": 654,
"repairTotCount": 4,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4497",
"name": "The American practice abridged, or the family physician: Being the scientific system of medicines",
"groupPageCount": 25,
"aveMonthlyViewsGr": 42,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4498",
"name": "A dictionary of medical terminology, dental surgery, and the collateral sciences",
"groupPageCount": 12,
"aveMonthlyViewsGr": 0,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4493",
"name": "The family physician: Comprising rules for the prevention and cure of diseases",
"groupPageCount": 9,
"aveMonthlyViewsGr": 715,
"repairTotCount": 2,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4502",
"name": "Anaesthetics: Brief of points and proofs in support of petitions, memorials, resolutions and letters",
"groupPageCount": 7,
"aveMonthlyViewsGr": 14,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4435",
"name": "On ecclecticism in medicine, or: A critical review of the leading medical doctrines",
"groupPageCount": 6,
"aveMonthlyViewsGr": 1,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4325",
"name": "Synopsis of lectures on obstetrics and the forms of disease peculiar to women and children",
"groupPageCount": 4,
"aveMonthlyViewsGr": 2646,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4469",
"name": "Notes on the cholera at Varna in 1854",
"groupPageCount": 2,
"aveMonthlyViewsGr": 324464,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4171",
"name": "Have we the best possible ambulance system?",
"groupPageCount": 1,
"aveMonthlyViewsGr": 3483,
"repairTotCount": 0,
"url": "to-do_list.html"
} ,
{
"pageGroupID": "4480",
"name": "The ship and shore physician and surgeon",
"groupPageCount": 1,
"aveMonthlyViewsGr": 83,
"repairTotCount": 0,
"url": "to-do_list.html"
}
]
}
]}
<!doctype html>
<!-- Based on: http://flowingdata.com/2014/07/02/jobs-charted-by-state-and-salary/
license: gpl-3.0
-->
<html>
<head>
<meta charset="utf-8">
<title>Interactive Treemap with Shading for Severity</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
/* Normalize */
body, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form, fieldset, input, p, blockquote, th, td, legend { margin: 0; padding: 0;} /*div,*/
h1,h2,h3,h4,h5,h6 { font-family: Helvetica, Arial, sans-serif; font-weight: bold; }
address, caption, cite, code, dfn, th, var { font-style: normal; font-weight: normal; }
table { border-collapse: collapse; border-spacing: 0; }
fieldset,img { border: 0; }
caption,th { text-align: left; }
/* @end Normalize */
/* General */
body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; margin: auto; margin-left:1em; position: relative; width: 954px; }
p, ul { margin-top:1em; }
/*form { font-size: 12px; }*/
.clr { clear: both; }
/* @end General */
.container { margin-left:100px; }
/* Filters */
#filters { height: 4em; margin-top:2em; }
#filters label { font-size: 90%; /*font-weight: bold; */ }
#filters #leftfilters { float: left; width: 130px; border-right: 1px solid #e0e0e0; height: 80px; margin-right: 10px; }
#filters #categorySelect { margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px solid #e0e0e0; }
#filters #rightfilters { width: 780px; float: left; }
/* @end Filters */
/* Progress loader */
#loadprogress { position: relative; top: -350px; left: 470px; width: 100px; text-align: center; font-size: 85%; text-transform: uppercase; line-height: 1.4em; }
/* @end Progress loader */
/* Treemap */
.node { border: solid 1px #999; /* org was .3px */ font: 10px Helvetica, sans-serif; text-align: left; vertical-align: middle; line-height: 1.2em; overflow: hidden; position: absolute; color: #333333; cursor: default; background: #fff; }
.parent.node { border: 1px solid #999; background: #999; }
.node .childlabel { padding: 2px 0px 0 4px; color: gray; /* ligher would be c3c3c3; gray:808080 */ }
.parentlabel { position: absolute; text-transform: uppercase; font-family: Helvetica, Arial, sans-serif; font-size: 16px; font-weight: bold; padding: 10px; overflow: hidden; color: #9a16bf; pointer-events: none; display: none; }
#occundefined { background: #999; }
/* child boxes that become shaded. If gradient is added, go dark and make this text white */
.node.highlight {
background: #f2dede; /* shade of blocks. Green is d1f77b, red, bootstrap warning: f2dede; pink:ffc0cb, LightCoral:F08080 */
color: #000; /* Options for text darkness: black, gray, c3c3c3 */
font-weight: bold;
}
.node.highlight:hover, .node:hover { background: #ff6; }
.node.selected { background: #ff6; color: #000; }
.parent.node:hover { background: #999; }
/* @end Treemap */
/* Slider */
.axis { font-size: 85%; -webkit-user-select: none; -moz-user-select: none; user-select: none; }
.axis .domain { fill: none; stroke: #333; stroke-opacity: .4; stroke-width: 7.5px; stroke-linecap: square; }
.slider .highlighter {
fill: #999999; /* OLD: d1f77b. change this if you change the box shade */
stroke: #999999;
/* fill: black;*/
/* stroke: black;*/
}
.axis .halo { fill: none; stroke: #fff; stroke-width: 6px; stroke-linecap: square; }
.slider .handle { fill: #fff; stroke: #000; stroke-opacity: .5; stroke-width: 1.25px; pointer-events: none; }
/* @end Slider */
/* Tooltip */
#tooltip { position:absolute; width: 200px; line-height: 1.1em; height: auto; padding: 8px; background-color: #fff; pointer-events: none; font-size: 12px; -webkit-box-shadow: 4px 4px 10px rgba(0,0,0,0.4); -moz-box-shadow: 4px 4px 10px rgba(0,0,0,0.4); box-shadow: 4px 4px 10px rgba(0,0,0,0.4); border: 1px solid #ddd; }
#tooltip.hidden { display: none; }
#tooltip #contGroup { font-size: 9px; font-weight: normal; margin-bottom: 4px; text-transform: uppercase; }
#tooltip #groupTitle { font-weight: bold; margin-bottom: 8px; border-bottom: 1px solid #000; padding-bottom: 7px; }
#tooltip #pagesPoints { text-align: center; }
#tooltip #groupTraffic { margin-bottom: 5px; text-align: center; }
/* @end Tooltip */
.facetReportButton { padding:6px 6px 8px 6px; margin: 0 4px 0 4px; }
.facetReportButton span { margin-left:.3em; }
#credits { font-size: 10px; text-align: right; }
ul, li { margin-left:1em; }
</style>
</head>
<body>
<h2>Visualize and Accelerate Web Site Repairs</h2>
<p>Rectangle volume shows relative number of HTML pages; some communication packages are small; some are large.
Gray shading plus zero points means no errors were found. A shade of blue plus total count of points
shows severity of the problem being analyzed. White indicates that the microsite was not accessed during the
period for which traffic data was taken.</p>
<div id="filters">
<div id="leftfilters">
<div id="showRepairCats">
<form>
<input type="checkbox" name="parentcheck" value="on" id="parentcheck"/>
<label for="parentcheck"><strong>Show topics</strong></label>
</form>
</div><!-- end showRepairCats -->
</div><!-- end leftfilters -->
<div id="rightfilters">
<div id="sliderholder">
<label>Drag the traffic slider (below) all the way left; this is your <strong><em>total severity</em></strong> for all problems everywhere, for any amount of traffic. Then drag all the way right; dragging back left, you will see the highest-traffic properties appear, red = problems; green = OK. Score in points appears after each title. Default = 5,000 page views/mo.</label>
</div>
</div><!-- end rightfilters -->
<div class="clr"></div>
</div><!-- end filters -->
<div id="groups">
<div id="treemapholder"></div>
<div id="tooltip" class="hidden">
<div id="contGroup"></div>
<div id="groupTitle"></div>
<div id="groupTraffic"></div>
<div id="pagesPoints"></div>
</div>
<div id="loadprogress">Loading Data&hellip;<br /><span class="progvalue">0</span>%</div>
</div><!-- end #groups -->
<script>
var margin = {top: 40, right: 0, bottom: 10, left: 0},
width = 900,
height = 475;
var boxVolumeDimension = 'groupPageCount';
var boxShadingDimension = 'repairTotCount'
var medCutoff = 5000;
var showParentLab = false;
var treemap = d3.layout.treemap()
.size([width, height])
.sticky(true)
.padding(2)
.sort(function(a, b) { return a.value - b.value; })
.value(function(d) { return d[boxVolumeDimension]; });
var div = d3.select("#treemapholder").append("div")
.attr("id", "treemap")
.style("position", "relative")
.style("width", (width + margin.left + margin.right) + "px")
.style("height", (height + margin.top + margin.bottom) + "px")
.style("left", margin.left + "px")
.style("top", margin.top + "px");
var colorScale
// --- Load data ---------------------------------------------------------------
d3.json("exampleData.json")
// Show loading progress for data file
.on("progress", function() {
d3.select("#loadprogress .progvalue").text(Math.round(100 * d3.event.loaded / 1075072)); // count was based on size of HUGE orig json file
})
.get(function(error, root) {
var maxshade = 0;
for (var i = 0; i < root.children.length; i++) {
var innerChildren = root.children[i];
for (var j = 0; j < innerChildren.children.length; j++) {
var curRepairTotCount = innerChildren.children[j].repairTotCount;
if (curRepairTotCount > maxshade) maxshade = curRepairTotCount;
}
}
colorScale = d3.scale.linear()
.range(['#9ECAE1', '#08306B']) // pink to maroon, #FFA07A', '#800000'. dark red: a94442. See https://en.wikipedia.org/wiki/Web_colors
.domain([1, maxshade]);
d3.select("#loadprogress").remove();
// node: Load rectangles, arrange, highlight
var node = div.datum(root).selectAll(".node")
.data(treemap.nodes)
.enter().append("div")
.attr("class", function(d) { return d.children ? "parent node" : "node"; })
.attr("id", function(d) { return 'pagegroupnode' + d.pageGroupID })
.call(position);
// childlabel: microsite name and total points.
var label = node.append("div")
.attr("class", "childlabel")
.text(function(d) {
if (d.children || d.dx*d.dy < 800) { return null; }
else { return d.name + ' (' + numberWithCommas(d.repairTotCount) + ')' } });
// .parentlabel: Append labels to parent rectangles upon request - "Show topics"
d3.selectAll(".parent").data().map(function(d) {
if (d.name === "contentByGroup") return null;
if (d.dx <= 22) return null;
if (d.dy <= 22) return null;
div.append("div")
.text(d.name)
.style("left", d.x + "px")
.style("top", d.y + "px")
.style("width", (d.dx-22) + "px")
.style("height", (d.dy-22) + "px")
.attr("class", "parentlabel")
.attr("id", "plab"+d.pageGroupID);
});
//Color, shading of boxes
node
.attr("class", medHighlight)
.style("background", getColor);
//.style("fill", function(d) { return colorScale(d.value)});
// Repair category selection
var select = d3.selectAll("select").on("change", function change() {
// Update nodes to new state
boxVolumeDimension = this.value;
var value = function(d) { return d[boxVolumeDimension]; }
node
.data(treemap.value(value).nodes)
.transition()
.duration(1000)
.call(position)
.call(positionParentLabels);
// Adjust content group labels
node.selectAll("div.childlabel")
.text(function(d) {
if (d.children || d.dx*d.dy < 800) { return null; }
else { return d.name; }
});
// Adjust box-traffic highlighting
node
.attr("class", medHighlight);
});
// Adjust parent labels on checkbox
d3.selectAll("#parentcheck").on("change", function change() {
showParentLab = this.checked;
d3.selectAll(".parentlabel")
.call(positionParentLabels);
});
// Retrieve CSV for a content group
node.on("click", function(d){
if(!d.children){
window.open(d.url, '_top'); // window.open(d.url) for new tab; _top or _parent, page will open in same window
}});
// Tooltip -----------------------------------------------------------------------------------
node.on("mouseover", function(d) {
if (!d.children) {
// Content group name
d3.select("#tooltip #contGroup").text(d.parent.name);
d3.select("#tooltip #groupTitle").text(d.name);
// Traffic count
if (d.aveMonthlyViewsGr == -99) {
var trafficText = "Estimate unavailable";
} else if (d.aveMonthlyViewsGr > 180000) {
var trafficText = "More than 180,000 per month"
} else {
var trafficText = numberWithCommas(d.aveMonthlyViewsGr) + " Ave views/month"
}
d3.select("#tooltip #groupTraffic").text(trafficText);
// Page count and point total
var numericResultText = numberWithCommas(d[boxVolumeDimension]) + " pages" + " / " + numberWithCommas(d.repairTotCount) + " points";
d3.select("#tooltip #pagesPoints").text(numericResultText);
// Adjust the width and height
var w = d3.select("#tooltip").style("width");
var h = d3.select("#tooltip").attr("height");
// Get mouse position and then adjust tooltip position accordingly
var groupsNode = d3.select("#groups").node();
var absMousePos = d3.mouse(groupsNode);
if (absMousePos[0] > 700) {
var xpos = absMousePos[0] - parseFloat(w) - 30;
} else {
var xpos = absMousePos[0] + 10;
}
if (absMousePos[1] > 400) {
var ypos = absMousePos[1] - 50;
} else {
var ypos = absMousePos[1] - 20;
}
d3.select("#tooltip")
.style("left", xpos + "px")
.style("top", ypos + "px");
// Display the tooltip
d3.select("#tooltip").classed("hidden", false);
}
})
.on("mouseout", function(d) {
// Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});
// Slider -----------------------------------------------------------------------------------
var slideMargin = {top: 0, right: 30, bottom: 10, left: 8},
slideWidth = 760 - slideMargin.left - slideMargin.right, // was 773
slideHeight = 45 - slideMargin.bottom - slideMargin.top; // was 75
var x = d3.scale.linear()
.domain([1, 100000])
.range([0, slideWidth])
.clamp(true);
var brush = d3.svg.brush()
.x(x)
.extent([0,0])
.on("brush", brushed);
var svg = d3.select("#sliderholder").append("svg")
.attr("width", slideWidth + slideMargin.left + slideMargin.right)
.attr("height", slideHeight + slideMargin.top + slideMargin.bottom)
.append("g")
.attr("transform", "translate(" + slideMargin.left + "," + slideMargin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + slideHeight / 2 + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(function(d) {
if (d == 100000) return '>' + numberWithCommas(d);
else return numberWithCommas(d);
})
.tickSize(0)
.tickPadding(12))
.select(".domain")
.select(function() { return this.parentNode.appendChild(this.cloneNode(true)); })
.attr("class", "halo");
var slider = svg.append("g")
.attr("class", "slider")
.call(brush);
slider.selectAll(".extent,.resize")
.remove();
slider.select(".background")
.attr("height", slideHeight);
var highlighter = slider.append("rect")
.attr("class", "highlighter")
.attr("height", 4)
.attr("x", x(medCutoff))
.attr("width", slideWidth-x(medCutoff)+2)
.attr("transform", "translate(0," + (slideHeight / 2 - 2) + ")");
var handle = slider.append("circle")
.attr("class", "handle")
.attr("transform", "translate(0," + slideHeight / 2 + ")")
.attr("r", 7)
.attr("cx", x(medCutoff));
// Functions -----------------------------------------------------------------------------------
// Adjust node colors accordingly when traffic slider is moved
function brushed() {
var value = brush.extent()[0];
if (d3.event.sourceEvent) { // not a programmatic event
value = x.invert(d3.mouse(this)[0]);
brush.extent([value, value]);
}
handle.attr("cx", x(value));
highlighter
.attr("x", x(value))
.attr("width", slideWidth-x(value)+2);
var highlight = function(d) {
var cssclass;
if (d.children) {
cssclass = "parent node";
} else if (d.aveMonthlyViewsGr > value && !d.children) {
cssclass = "node highlight";
} else {
cssclass = "node";
}
return cssclass;
}
node
.attr("class", highlight)
.style("background", getColor);
// Set the current traffic
medCutoff = value;
}
}); // end on load event
function position() {
this.style("left", function(d) { return d.x + "px"; })
.style("top", function(d) { return d.y + "px"; })
.style("width", function(d) { return Math.max(0, d.dx - 1) + "px"; })
.style("height", function(d) { return Math.max(0, d.dy - 1) + "px"; });
}
function positionParentLabels() {
d3.selectAll(".parent").data().map(function(d) {
d3.select("#plab" + d.pageGroupID)
.transition().duration(1000)
.style("left", d.x + "px")
.style("top", d.y + "px")
.style("width", (d.dx-22) + "px")
.style("height", (d.dy-22) + "px")
.style("display", function() { return d.dx > 22 && showParentLab ? "block" : "none" });
});
}
function medHighlight(d) {
if (d.children) {
return "parent node";
} else if (d.aveMonthlyViewsGr > medCutoff && !d.children) {
return "node highlight";
} else {
return "node";
}
}
// Color range
function getColor(d) {
if (d.children) {
return null;
} else if (d.aveMonthlyViewsGr > medCutoff && !d.children) {
if (d.repairTotCount < 1) return "#D3D3D3"; // old d1f77b
return colorScale(d.repairTotCount);
} else {
return null;
}
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment