D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
bumbeishvili
Full window
Github gist
Employees Hierarchy Chart using d3.js
Built with
blockbuilder.org
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <link rel="shortcut icon" type="image/x-icon" href="https://production-assets.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" /> <link rel="mask-icon" type="" href="https://production-assets.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" /> <title>CodePen - Redesigned - Company Employees Hierarchy Chart </title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel='stylesheet prefetch' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.css'> <link rel='stylesheet prefetch' href='https://fonts.googleapis.com/css?family=Roboto'> <style> .full-container { background-color: red; width: 100%; height: 100%; font-family: 'Roboto', sans-serif; } /* ######################## DEPARTMENT INFO ############################*/ .department-information { font-family: 'Roboto', sans-serif; display:none; box-shadow: 0 0 5px #999999; position: absolute; max-width: 200px; top: 60px; left: 20px; padding: 10px; background-color: white; } .department-information .dept-name { color: #26a69a; font-weight: bold; } .department-information .dept-description { margin-top: 10px; color: #959b9a; font-size: 13px; } .department-information .dept-emp-count { margin-top: 10px; color: #959b9a; font-size: 13px; } /* ############################## SEARCHBOX ######################################### */ .user-search-box { overflow: hidden; position: absolute; right: 0; height: 100%; top: 0; width: 0; background-color: white; border: 1px solid #c7dddb; font-family: 'Roboto', sans-serif; font-size: 14px; line-height: 1.5; } ::-webkit-input-placeholder { /* WebKit, Blink, Edge */ color: #bcbcc4; opacity: 0.5; } :-moz-placeholder { /* Mozilla Firefox 4 to 18 */ color: #bcbcc4; opacity: 0.5; } ::-moz-placeholder { /* Mozilla Firefox 19+ */ color: #bcbcc4; opacity: 0.5; } :-ms-input-placeholder { /* Internet Explorer 10-11 */ color: #bcbcc4; opacity: 0.5; } .user-search-box .input-box { width: 100%; height: 200px; top: 0; background-color: #e8efee; } .user-search-box .close-button-wrapper i { margin: 10px; margin-left: 9%; font-size: 60px; font-weight: 400; color: #aa1414; } .user-search-box input { color: gray !important; background-color: transparent; border: none; border-bottom: 1px solid #9e9e9e; border-radius: 0; outline: none; height: 3rem; width: 100%; font-size: 1rem; margin: 0 0 20px 0; padding: 0; box-shadow: none; box-sizing: content-box; transition: all 0.3s; } .user-search-box input:focus { border-bottom: 1px solid #26a69a; box-shadow: 0 1px 0 0 #26a69a; } .user-search-box .result-header { background-color: white; font-weight: 700; padding: 12px; color: gray; border-top: 2px solid #d3e8e5; border-bottom: 1px solid #d3e8e5; } .user-search-box .result-list { position: absolute; max-height: 100%; min-width: 100%; overflow: auto; } .user-search-box .buffer { width: 100%; height: 400px; } .user-search-box .list-item { clear: both; background-color: white; position: relative; background-color: white; width: 100%; height: 100px; border-top: 1px solid #d3e8e5; } .user-search-box .list-item a { display: inline; margin: 0; } .user-search-box .list-item .image-wrapper { float: left; width: 100px; height: 100px; } .user-search-box .list-item .image { width: 70px; height: 70px; margin-left: 15px; margin-top: 15px; border-radius: 5px; } .user-search-box .list-item .description { padding: 15px; padding-left: 0px; float: left; width: 180px; } .user-search-box .list-item .buttons { padding: 15px; padding-left: 0px; float: left; width: auto; } .user-search-box .list-item .description .name { font-size: 15px; color: #aa1414; font-weight: 900; margin: 0; padding: 0; letter-spacing: 1px; } .user-search-box .list-item .description .position-name { color: #59525b; letter-spacing: 1px; font-size: 12px; font-weight: 900; margin: 0; margin-top: 3px; padding: 0; } .user-search-box .list-item .description .area { color: #91a4a5; letter-spacing: 1px; font-size: 12px; font-weight: 400; margin: 0; margin-top: 3px; padding: 0; } .user-search-box .list-item .btn-locate{ margin-top:30px; } .user-search-box .list-item .btn-search-box{ font-size:10px; } .user-search-box .close-button-wrapper i:hover { color: black; cursor: pointer; } .user-search-box .input-wrapper { width: 80%; margin: 0 auto; } .user-search-box .input-bottom-placeholder { margin-top: -16px; color: #bcbcc4; letter-spacing: 1px; } /* ############################### Tooltip css ########################### */ .profile-image-wrapper { background-size: 210px; margin: 30px; border-radius: 50%; width: 210px; height: 210px; } .customTooltip-wrapper { font-family: 'Roboto', sans-serif; opacity: 0; /* NEW */ display: none; position: absolute; } .customTooltip { background: white; box-shadow: 0 0 5px #999999; color: #333; position: absolute; font-size: 12px; left: 130px; text-align: center; top: 95px; z-index: 10; text-align: left; } .tooltip-hr { width: 70px; background-color: #91a4a5; height: 1px; margin-left: auto; margin-right: auto; margin-top: -17px; margin-bottom: 25px; } .tooltip-desc { padding-left: 10px; margin-top: -20px; margin-left: 20px; overflow: auto; } .tooltip-desc .name { color: #962828; font-weight: 900; letter-spacing: 1px; font-size: 24px; font-weight: bold; margin-bottom: 2px; text-decoration: none; } .tooltip-desc .name:hover { text-decoration: underline; } .tooltip-desc .position { color: #59525b; letter-spacing: 1px; font-size: 17px; font-weight: 500; margin-bottom: 2px; margin-top: 0px; } .tooltip-desc .area { color: #91a4a5; letter-spacing: 1px; font-size: 16px; font-weight: 400; margin-bottom: 2px; margin-top: 7px; } .tooltip-desc .office { color: #91a4a5; line-height: 160%; font-size: 14px; font-weight: 400; margin-bottom: -10px; margin-top: -5px; } .tooltip-desc .tags-wrapper .title { display: inline-block; float: left; } .tooltip-desc .tags-wrapper .tags { display: inline-block; float: left; } .bottom-tooltip-hr { width: 100%; background-color: #58993e; height: 3px; margin-left: auto; margin-right: auto; margin-top: -17px; } .btn-tooltip-department { margin-top: 20px; } .btn.disabled { background-color: #DFDFDF !important; box-shadow: none; color: #9F9F9F !important; cursor: default; } .btn { border: none; border-radius: 2px; height: 36px; line-height: 36px; outline: 0; text-transform: uppercase; vertical-align: middle; -webkit-tap-highlight-color: transparent; text-decoration: none; color: #fff; background-color: #26a69a; text-align: center; letter-spacing: .5px; transition: .2s ease-out; cursor: pointer; box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12); } .btn:hover { box-shadow: 0 5px 11px 0 rgba(0, 0, 0, 0.18), 0 4px 15px 0 rgba(0, 0, 0, 0.15); } .btn.disabled:hover { box-shadow: none; } /* ####################################### TAGS ###################################### */ .tags { list-style: none; margin-top: -9px; margin-left: 5px; overflow: hidden; padding: 0; } .tags-wrapper { font-size: 2.28rem; line-height: 110%; margin: 1.14rem 0 0.912rem 0; } .tags-wrapper .title { color: #91a4a5; font-size: 24px; } .tags li { float: left; } .tag { font-size: 11px; background: #E1ECF4; border-radius: 2px; color: ##39739d; display: inline-block; height: 20px; line-height: 20px; padding: 0 5px 0 5px; position: relative; margin: 0 5px 5px 0; text-decoration: none; -webkit-transition: color 0.2s; } /* ############################# Buttons ############################################*/ .btn-search { top: 80px; } .btn-fullscreen { top: 20px; } .btn-back { top: 20px; left: 20px; display: none; } .btn-show-my-self { top: 50px; } .btn-action { position: absolute; right: 25px; height: 26px; color: white; background-color: #aa1414; border: 1px solid black; border-radius: 12px; cursor: pointer; font-size: 15px; font-family: 'Roboto', sans-serif; } .btn-action:focus { outline: 0; background-color: #aa1414; } .btn-action:hover { background-color: #490b0b; } .btn-action i { font-size: 14px; } .btn-action .icon { background-color: #c19e45; padding: 5px 6px 5px 6px; border-radius: 11px; margin-right: -7px; } /* ############################################## SVG ################################# */ .nodeHasChildren { fill: white; } .nodeDoesNotHaveChildren { fill: white; } .nodeRepresentsCurrentUser { stroke: Chartreuse; stroke-width: 3; } text { fill: dimgray; } .link { fill: none; stroke: #ccc; stroke-width: 1.5px; } .node { cursor: pointer; } .node-collapse { stroke: grey; } .node-collapse-right-rect { fill: #70c645; stroke: #70c645; } .node text { fill: white; font-family: "Segoe UI", Arial, sans-serif; font-size: 10px; } .node circle { stroke-width: 1px; stroke: #70c645; fill: #70c645; } .node-group .emp-name { fill: #962828; font-size: 12px; font-weight: 600 } .node-group .emp-position-name { fill: #59525b; font-size: 11px; } .node-group .emp-area { fill: #91a4a5; font-size: 10px; } .node-group .emp-count, .node-group .emp-count-icon { fill: #91a4a5; font-size: 12px; } </style> </head> <body translate="no" > <div id="full-container"> <button class="btn-action btn-fullscreen" onclick="params.funcs.toggleFullScreen()">Fullscreen <span class='icon'/> <i class="fa fa-arrows-alt" aria-hidden="true"></i></span></button> <button class="btn-action btn-show-my-self" onclick="params.funcs.showMySelf()"> Show myself <span class='icon'/> <i class="fa fa-user" aria-hidden="true"></i></span></button> <button class=" btn-action btn-search" onclick="params.funcs.search()"> Search <span class='icon'/> <i class="fa fa-search" aria-hidden="true"></i></span></button> <button class=" btn-action btn-back" onclick="params.funcs.back()"> Back <span class='icon'/> <i class="fa fa-arrow-left" aria-hidden="true"></i></span></button> <div class="department-information"> <div class="dept-name"> dept name </div> <div class="dept-emp-count"> dept description test, this is department description </div> <div class="dept-description"> dept description test, this is department description </div> </div> <div class="user-search-box"> <div class="input-box"> <div class="close-button-wrapper"><i onclick="params.funcs.closeSearchBox()" class="fa fa-times" aria-hidden="true"></i></div> <div class="input-wrapper"> <input type="text" class="search-input" placeholder="Search" /> <div class="input-bottom-placeholder">By Firstname, Lastname, Tags</div> </div> <div> </div> </div> <div class="result-box"> <div class="result-header"> RESULTS </div> <div class="result-list"> <div class="buffer"></div> </div> </div> </div> <div id="svgChart"></div> <!-- <button class="btn btn-expand" onclick="params.funcs.expandAll()">Expand All</button> --> </div> <script src='https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'></script> <script > var params = { selector: "#svgChart", dataLoadUrl: "https://raw.githubusercontent.com/bumbeishvili/Assets/master/Projects/D3/Organization%20Chart/redesignedChartLongData.json", chartWidth: window.innerWidth-40, chartHeight: window.innerHeight - 40, funcs: { showMySelf: null, search: null, closeSearchBox: null, clearResult: null, findInTree: null, reflectResults: null, departmentClick: null, back: null, toggleFullScreen: null, locate:null }, data: null } d3.json(params.dataLoadUrl, function(data) { params.data = data; params.pristinaData = JSON.parse(JSON.stringify(data)); drawOrganizationChart(params); }) function drawOrganizationChart(params) { listen(); params.funcs.showMySelf = showMySelf; params.funcs.expandAll = expandAll; params.funcs.search = searchUsers; params.funcs.closeSearchBox = closeSearchBox; params.funcs.findInTree = findInTree; params.funcs.clearResult = clearResult; params.funcs.reflectResults = reflectResults; params.funcs.departmentClick = departmentClick; params.funcs.back = back; params.funcs.toggleFullScreen = toggleFullScreen; params.funcs.locate=locate; var attrs = { EXPAND_SYMBOL: '\uf067', COLLAPSE_SYMBOL: '\uf068', selector: params.selector, root: params.data, width: params.chartWidth, height: params.chartHeight, index: 0, nodePadding: 9, collapseCircleRadius: 7, nodeHeight: 80, nodeWidth: 210, duration: 750, rootNodeTopMargin: 20, minMaxZoomProportions: [0.05, 3], linkLineSize: 180, collapsibleFontSize: '10px', userIcon: '\uf007', nodeStroke: "#ccc", nodeStrokeWidth: '1px' } var dynamic = {} dynamic.nodeImageWidth = attrs.nodeHeight * 100 / 140; dynamic.nodeImageHeight = attrs.nodeHeight - 2 * attrs.nodePadding; dynamic.nodeTextLeftMargin = attrs.nodePadding * 2 + dynamic.nodeImageWidth dynamic.rootNodeLeftMargin = attrs.width / 2; dynamic.nodePositionNameTopMargin = attrs.nodePadding + 8 + dynamic.nodeImageHeight / 4 * 1 dynamic.nodeChildCountTopMargin = attrs.nodePadding + 14 + dynamic.nodeImageHeight / 4 * 3 var tree = d3.layout.tree().nodeSize([attrs.nodeWidth + 40, attrs.nodeHeight]); var diagonal = d3.svg.diagonal() .projection(function(d) { debugger; return [d.x + attrs.nodeWidth / 2, d.y + attrs.nodeHeight / 2]; }); var zoomBehaviours = d3.behavior .zoom() .scaleExtent(attrs.minMaxZoomProportions) .on("zoom", redraw); var svg = d3.select(attrs.selector) .append("svg") .attr("width", attrs.width) .attr("height", attrs.height) .call(zoomBehaviours) .append("g") .attr("transform", "translate(" + attrs.width / 2 + "," + 20 + ")"); //necessary so that zoom knows where to zoom and unzoom from zoomBehaviours.translate([dynamic.rootNodeLeftMargin, attrs.rootNodeTopMargin]); attrs.root.x0 = 0; attrs.root.y0 = dynamic.rootNodeLeftMargin; if (params.mode != 'department') { // adding unique values to each node recursively var uniq = 1; addPropertyRecursive('uniqueIdentifier', function(v) { return uniq++; }, attrs.root); } expand(attrs.root); if (attrs.root.children) { attrs.root.children.forEach(collapse); } update(attrs.root); d3.select(attrs.selector).style("height", attrs.height); var tooltip = d3.select('body') .append('div') .attr('class', 'customTooltip-wrapper'); function update(source, param) { // Compute the new tree layout. var nodes = tree.nodes(attrs.root) .reverse(), links = tree.links(nodes); // Normalize for fixed-depth. nodes.forEach(function(d) { d.y = d.depth * attrs.linkLineSize; }); // Update the nodes… var node = svg.selectAll("g.node") .data(nodes, function(d) { return d.id || (d.id = ++attrs.index); }); // Enter any new nodes at the parent's previous position. var nodeEnter = node.enter() .append("g") .attr("class", "node") .attr("transform", function(d) { return "translate(" + source.x0 + "," + source.y0 + ")"; }) var nodeGroup = nodeEnter.append("g") .attr("class", "node-group") nodeGroup.append("rect") .attr("width", attrs.nodeWidth) .attr("height", attrs.nodeHeight) .attr("data-node-group-id",function(d){ return d.uniqueIdentifier; }) .attr("class", function(d) { var res = ""; if (d.isLoggedUser) res += 'nodeRepresentsCurrentUser '; res += d._children || d.children ? "nodeHasChildren" : "nodeDoesNotHaveChildren"; return res; }); var collapsiblesWrapper = nodeEnter.append('g') .attr('data-id', function(v) { return v.uniqueIdentifier; }); var collapsibleRects = collapsiblesWrapper.append("rect") .attr('class', 'node-collapse-right-rect') .attr('height', attrs.collapseCircleRadius) .attr('fill', 'black') .attr('x', attrs.nodeWidth - attrs.collapseCircleRadius) .attr('y', attrs.nodeHeight - 7) .attr("width", function(d) { if (d.children || d._children) return attrs.collapseCircleRadius; return 0; }) var collapsibles = collapsiblesWrapper.append("circle") .attr('class', 'node-collapse') .attr('cx', attrs.nodeWidth - attrs.collapseCircleRadius) .attr('cy', attrs.nodeHeight - 7) .attr("", setCollapsibleSymbolProperty); //hide collapse rect when node does not have children collapsibles.attr("r", function(d) { if (d.children || d._children) return attrs.collapseCircleRadius; return 0; }) .attr("height", attrs.collapseCircleRadius) collapsiblesWrapper.append("text") .attr('class', 'text-collapse') .attr("x", attrs.nodeWidth - attrs.collapseCircleRadius) .attr('y', attrs.nodeHeight - 3) .attr('width', attrs.collapseCircleRadius) .attr('height', attrs.collapseCircleRadius) .style('font-size', attrs.collapsibleFontSize) .attr("text-anchor", "middle") .style('font-family', 'FontAwesome') .text(function(d) { return d.collapseText; }) collapsiblesWrapper.on("click", click); nodeGroup.append("text") .attr("x", dynamic.nodeTextLeftMargin) .attr("y", attrs.nodePadding + 10) .attr('class', 'emp-name') .attr("text-anchor", "left") .text(function(d) { return d.name.trim(); }) .call(wrap, attrs.nodeWidth); nodeGroup.append("text") .attr("x", dynamic.nodeTextLeftMargin) .attr("y", dynamic.nodePositionNameTopMargin) .attr('class', 'emp-position-name') .attr("dy", ".35em") .attr("text-anchor", "left") .text(function(d) { var position = d.positionName.substring(0,27); if(position.length<d.positionName.length){ position = position.substring(0,24)+'...' } return position; }) nodeGroup.append("text") .attr("x", dynamic.nodeTextLeftMargin) .attr("y", attrs.nodePadding + 10 + dynamic.nodeImageHeight / 4 * 2) .attr('class', 'emp-area') .attr("dy", ".35em") .attr("text-anchor", "left") .text(function(d) { return d.area; }) nodeGroup.append("text") .attr("x", dynamic.nodeTextLeftMargin) .attr("y", dynamic.nodeChildCountTopMargin) .attr('class', 'emp-count-icon') .attr("text-anchor", "left") .style('font-family', 'FontAwesome') .text(function(d) { if (d.children || d._children) return attrs.userIcon; }); nodeGroup.append("text") .attr("x", dynamic.nodeTextLeftMargin + 13) .attr("y", dynamic.nodeChildCountTopMargin) .attr('class', 'emp-count') .attr("text-anchor", "left") .text(function(d) { if (d.children) return d.children.length; if (d._children) return d._children.length; return; }) nodeGroup.append("defs").append("svg:clipPath") .attr("id", "clip") .append("svg:rect") .attr("id", "clip-rect") .attr("rx", 3) .attr('x', attrs.nodePadding) .attr('y', 2 + attrs.nodePadding) .attr('width', dynamic.nodeImageWidth) .attr('fill', 'none') .attr('height', dynamic.nodeImageHeight - 4) nodeGroup.append("svg:image") .attr('y', 2 + attrs.nodePadding) .attr('x', attrs.nodePadding) .attr('preserveAspectRatio', 'none') .attr('width', dynamic.nodeImageWidth) .attr('height', dynamic.nodeImageHeight - 4) .attr('clip-path', "url(#clip)") .attr("xlink:href", function(v) { return v.imageUrl; }) // Transition nodes to their new position. var nodeUpdate = node.transition() .duration(attrs.duration) .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) //todo replace with attrs object nodeUpdate.select("rect") .attr("width", attrs.nodeWidth) .attr("height", attrs.nodeHeight) .attr('rx', 3) .attr("stroke", function(d){ if(param && d.uniqueIdentifier== param.locate){ return '#a1ceed' } return attrs.nodeStroke; }) .attr('stroke-width', function(d){ if(param && d.uniqueIdentifier== param.locate){ return 6; } return attrs.nodeStrokeWidth}) // Transition exiting nodes to the parent's new position. var nodeExit = node.exit().transition() .duration(attrs.duration) .attr("transform", function(d) { return "translate(" + source.x + "," + source.y + ")"; }) .remove(); nodeExit.select("rect") .attr("width", attrs.nodeWidth) .attr("height", attrs.nodeHeight) // Update the links… var link = svg.selectAll("path.link") .data(links, function(d) { return d.target.id; }); // Enter any new links at the parent's previous position. link.enter().insert("path", "g") .attr("class", "link") .attr("x", attrs.nodeWidth / 2) .attr("y", attrs.nodeHeight / 2) .attr("d", function(d) { var o = { x: source.x0, y: source.y0 }; return diagonal({ source: o, target: o }); }); // Transition links to their new position. link.transition() .duration(attrs.duration) .attr("d", diagonal) ; // Transition exiting nodes to the parent's new position. link.exit().transition() .duration(attrs.duration) .attr("d", function(d) { var o = { x: source.x, y: source.y }; return diagonal({ source: o, target: o }); }) .remove(); // Stash the old positions for transition. nodes.forEach(function(d) { d.x0 = d.x; d.y0 = d.y; }); if(param && param.locate){ var x; var y; nodes.forEach(function(d) { if (d.uniqueIdentifier == param.locate) { x = d.x; y = d.y; } }); // normalize for width/height var new_x = (-x + (window.innerWidth / 2)); var new_y = (-y + (window.innerHeight / 2)); // move the main container g svg.attr("transform", "translate(" + new_x + "," + new_y + ")") zoomBehaviours.translate([new_x, new_y]); zoomBehaviours.scale(1); } if (param && param.centerMySelf) { var x; var y; nodes.forEach(function(d) { if (d.isLoggedUser) { x = d.x; y = d.y; } }); // normalize for width/height var new_x = (-x + (window.innerWidth / 2)); var new_y = (-y + (window.innerHeight / 2)); // move the main container g svg.attr("transform", "translate(" + new_x + "," + new_y + ")") zoomBehaviours.translate([new_x, new_y]); zoomBehaviours.scale(1); } /*################ TOOLTIP #############################*/ function getTagsFromCommaSeparatedStrings(tags) { return tags.split(',').map(function(v) { return '<li><div class="tag">' + v + '</div></li> ' }).join(''); } function tooltipContent(item) { var strVar = ""; strVar += " <div class=\"customTooltip\">"; strVar += " <!--"; strVar += " <div class=\"tooltip-image-wrapper\"> <img width=\"300\" src=\"https:\/\/raw.githubusercontent.com\/bumbeishvili\/Assets\/master\/Projects\/D3\/Organization%20Chart\/cto.jpg\"> <\/div>"; strVar += "-->"; strVar += " <div class=\"profile-image-wrapper\" style='background-image: url(" + item.imageUrl + ")'>"; strVar += " <\/div>"; strVar += " <div class=\"tooltip-hr\"><\/div>"; strVar += " <div class=\"tooltip-desc\">"; strVar += " <a class=\"name\" href='" + item.profileUrl + "' target=\"_blank\"> " + item.name + "<\/a>"; strVar += " <p class=\"position\">" + item.positionName + " <\/p>"; strVar += " <p class=\"area\">" + item.area + " <\/p>"; strVar += ""; strVar += " <p class=\"office\">" + item.office + "<\/p>"; strVar += " <button class='" + (item.unit.type == 'business' ? " disabled " : "") + " btn btn-tooltip-department' onclick='params.funcs.departmentClick(" + JSON.stringify(item.unit) + ")'>" + item.unit.value + "</button>"; strVar += " <h4 class=\"tags-wrapper\"> <span class=\"title\"><i class=\"fa fa-tags\" aria-hidden=\"true\"><\/i>"; strVar += " "; strVar += " <\/span> <ul class=\"tags\">" + getTagsFromCommaSeparatedStrings(item.tags) + "<\/ul> <\/h4> <\/div>"; strVar += " <div class=\"bottom-tooltip-hr\"><\/div>"; strVar += " <\/div>"; strVar += ""; return strVar; } function tooltipHoverHandler(d) { var content = tooltipContent(d); tooltip.html(content); tooltip.transition() .duration(200).style("opacity", "1").style('display', 'block'); d3.select(this).attr('cursor', 'pointer').attr("stroke-width", 50); var y = d3.event.pageY; var x = d3.event.pageX; //restrict tooltip to fit in borders if (y < 220) { y += 220 - y; x += 130; } if(y>attrs.height-300){ y-=300-(attrs.height-y); } tooltip.style('top', (y - 300) + 'px') .style('left', (x - 470) + 'px'); } function tooltipOutHandler() { tooltip.transition() .duration(200) .style('opacity', '0').style('display', 'none'); d3.select(this).attr("stroke-width", 5); } nodeGroup.on('click', tooltipHoverHandler); nodeGroup.on('dblclick', tooltipOutHandler); function equalToEventTarget() { return this == d3.event.target; } d3.select("body").on("click", function() { var outside = tooltip.filter(equalToEventTarget).empty(); if (outside) { tooltip.style('opacity', '0').style('display', 'none'); } }); } // Toggle children on click. function click(d) { d3.select(this).select("text").text(function(dv) { if (dv.collapseText == attrs.EXPAND_SYMBOL) { dv.collapseText = attrs.COLLAPSE_SYMBOL } else { if (dv.children) { dv.collapseText = attrs.EXPAND_SYMBOL } } return dv.collapseText; }) if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } update(d); } //######################################################## //Redraw for zoom function redraw() { //console.log("here", d3.event.translate, d3.event.scale); svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")"); } // ############################# Function Area ####################### function wrap(text, width) { text.each(function() { var text = d3.select(this), words = text.text().split(/\s+/).reverse(), word, line = [], lineNumber = 0, lineHeight = 1.1, // ems x = text.attr("x"), y = text.attr("y"), dy = 0, //parseFloat(text.attr("dy")), tspan = text.text(null) .append("tspan") .attr("x", x) .attr("y", y) .attr("dy", dy + "em"); while (word = words.pop()) { line.push(word); tspan.text(line.join(" ")); if (tspan.node().getComputedTextLength() > width) { line.pop(); tspan.text(line.join(" ")); line = [word]; tspan = text.append("tspan") .attr("x", x) .attr("y", y) .attr("dy", ++lineNumber * lineHeight + dy + "em") .text(word); } } }); } function addPropertyRecursive(propertyName, propertyValueFunction, element) { if (element[propertyName]) { element[propertyName] = element[propertyName] + ' ' + propertyValueFunction(element); } else { element[propertyName] = propertyValueFunction(element); } if (element.children) { element.children.forEach(function(v) { addPropertyRecursive(propertyName, propertyValueFunction, v) }) } if (element._children) { element._children.forEach(function(v) { addPropertyRecursive(propertyName, propertyValueFunction, v) }) } } function departmentClick(item) { hide(['.customTooltip-wrapper']); if (item.type == 'department' && params.mode != 'department') { //find third level department head user var found = false; var secondLevelChildren = params.pristinaData.children; parentLoop: for (var i = 0; i < secondLevelChildren.length; i++) { var secondLevelChild = secondLevelChildren[i]; var thirdLevelChildren = secondLevelChild.children ? secondLevelChild.children : secondLevelChild._children; for (var j = 0; j < thirdLevelChildren.length; j++) { var thirdLevelChild = thirdLevelChildren[j]; if (thirdLevelChild.unit.value.trim() == item.value.trim()) { clear(params.selector); hide(['.btn-action']); show(['.btn-action.btn-back', '.btn-action.btn-fullscreen', '.department-information']); set('.dept-name', item.value); set('.dept-emp-count', "Employees Quantity - " + getEmployeesCount(thirdLevelChild)); set('.dept-description', thirdLevelChild.unit.desc); params.oldData = params.data; params.data = deepClone(thirdLevelChild); found = true; break parentLoop; } } } if (found) { params.mode = "department"; params.funcs.closeSearchBox(); drawOrganizationChart(params); } } } function getEmployeesCount(node) { var count = 1; countChilds(node); return count; function countChilds(node) { var childs = node.children ? node.children : node._children; if (childs) { childs.forEach(function(v) { count++; countChilds(v); }) } } } function reflectResults(results) { var htmlStringArray = results.map(function(result) { var strVar = ""; strVar += " <div class=\"list-item\">"; strVar += " <a >"; strVar += " <div class=\"image-wrapper\">"; strVar += " <img class=\"image\" src=\"" + result.imageUrl + "\"\/>"; strVar += " <\/div>"; strVar += " <div class=\"description\">"; strVar += " <p class=\"name\">" + result.name + "<\/p>"; strVar += " <p class=\"position-name\">" + result.positionName + "<\/p>"; strVar += " <p class=\"area\">" + result.area + "<\/p>"; strVar += " <\/div>"; strVar += " <div class=\"buttons\">"; strVar += " <a target='_blank' href='"+result.profileUrl+"'><button class='btn-search-box btn-action'>View Profile<\/button><\/a>"; strVar += " <button class='btn-search-box btn-action btn-locate' onclick='params.funcs.locate("+result.uniqueIdentifier+")'>Locate <\/button>"; strVar += " <\/div>"; strVar += " <\/a>"; strVar += " <\/div>"; return strVar; }) var htmlString = htmlStringArray.join(''); params.funcs.clearResult(); var parentElement = get('.result-list'); var old = parentElement.innerHTML; var newElement = htmlString + old; parentElement.innerHTML = newElement; set('.user-search-box .result-header', "RESULT - " + htmlStringArray.length); } function clearResult() { set('.result-list', '<div class="buffer" ></div>'); set('.user-search-box .result-header', "RESULT"); } function listen() { var input = get('.user-search-box .search-input'); input.addEventListener('input', function() { var value = input.value ? input.value.trim() : ''; if (value.length < 3) { params.funcs.clearResult(); } else { var searchResult = params.funcs.findInTree(params.data, value); params.funcs.reflectResults(searchResult); } }); } function searchUsers() { d3.selectAll('.user-search-box') .transition() .duration(250) .style('width', '350px') } function closeSearchBox() { d3.selectAll('.user-search-box') .transition() .duration(250) .style('width', '0px') .each("end", function() { params.funcs.clearResult(); clear('.search-input'); }); } function findInTree(rootElement, searchText) { var result = []; // use regex to achieve case insensitive search and avoid string creation using toLowerCase method var regexSearchWord = new RegExp(searchText, "i"); recursivelyFindIn(rootElement, searchText); return result; function recursivelyFindIn(user) { if (user.name.match(regexSearchWord) || user.tags.match(regexSearchWord)) { result.push(user) } var childUsers = user.children ? user.children : user._children; if (childUsers) { childUsers.forEach(function(childUser) { recursivelyFindIn(childUser, searchText) }) } }; } function back() { show(['.btn-action']); hide(['.customTooltip-wrapper', '.btn-action.btn-back', '.department-information']) clear(params.selector); params.mode = "full"; params.data = deepClone(params.pristinaData) drawOrganizationChart(params); } function expandAll() { expand(root); update(root); } function expand(d) { if (d.children) { d.children.forEach(expand); } if (d._children) { d.children = d._children; d.children.forEach(expand); d._children = null; } if (d.children) { // if node has children and it's expanded, then display - setToggleSymbol(d, attrs.COLLAPSE_SYMBOL); } } function collapse(d) { if (d._children) { d._children.forEach(collapse); } if (d.children) { d._children = d.children; d._children.forEach(collapse); d.children = null; } if (d._children) { // if node has children and it's collapsed, then display + setToggleSymbol(d, attrs.EXPAND_SYMBOL); } } function setCollapsibleSymbolProperty(d) { if (d._children) { d.collapseText = attrs.EXPAND_SYMBOL; } else if (d.children) { d.collapseText = attrs.COLLAPSE_SYMBOL; } } function setToggleSymbol(d, symbol) { d.collapseText = symbol; d3.select("*[data-id='" + d.uniqueIdentifier + "']").select('text').text(symbol); } /* recursively find logged user in subtree */ function findmySelf(d) { if (d.isLoggedUser) { expandParents(d); } else if (d._children) { d._children.forEach(function(ch) { ch.parent = d; findmySelf(ch); }) } else if (d.children) { d.children.forEach(function(ch) { ch.parent = d; findmySelf(ch); }); }; } function locateRecursive(d,id) { if (d.uniqueIdentifier == id) { expandParents(d); } else if (d._children) { d._children.forEach(function(ch) { ch.parent = d; locateRecursive(ch,id); }) } else if (d.children) { d.children.forEach(function(ch) { ch.parent = d; locateRecursive(ch,id); }); }; } /* expand current nodes collapsed parents */ function expandParents(d) { while (d.parent) { d = d.parent; if (!d.children) { d.children = d._children; d._children = null; setToggleSymbol(d, attrs.COLLAPSE_SYMBOL); } } } function toggleFullScreen() { if ((document.fullScreenElement && document.fullScreenElement !== null) || (!document.mozFullScreen && !document.webkitIsFullScreen)) { if (document.documentElement.requestFullScreen) { document.documentElement.requestFullScreen(); } else if (document.documentElement.mozRequestFullScreen) { document.documentElement.mozRequestFullScreen(); } else if (document.documentElement.webkitRequestFullScreen) { document.documentElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); } d3.select(params.selector + ' svg').attr('width', screen.width).attr('height', screen.height); } else { if (document.cancelFullScreen) { document.cancelFullScreen(); } else if (document.mozCancelFullScreen) { document.mozCancelFullScreen(); } else if (document.webkitCancelFullScreen) { document.webkitCancelFullScreen(); } d3.select(params.selector + ' svg').attr('width', params.chartWidth).attr('height', params.chartHeight); } } function showMySelf() { /* collapse all and expand logged user nodes */ if (!attrs.root.children) { if (!attrs.root.isLoggedUser) { attrs.root.children = attrs.root._children; } } if (attrs.root.children) { attrs.root.children.forEach(collapse); attrs.root.children.forEach(findmySelf); } update(attrs.root, {centerMySelf:true}); } //locateRecursive function locate(id){ /* collapse all and expand logged user nodes */ if (!attrs.root.children) { if (!attrs.root.uniqueIdentifier == id) { attrs.root.children = attrs.root._children; } } if (attrs.root.children) { attrs.root.children.forEach(collapse); attrs.root.children.forEach(function(ch){ locateRecursive(ch,id) }); } update(attrs.root, {locate:id}); } function deepClone(item) { return JSON.parse(JSON.stringify(item)); } function show(selectors) { display(selectors, 'initial') } function hide(selectors) { display(selectors, 'none') } function display(selectors, displayProp) { selectors.forEach(function(selector) { var elements = getAll(selector); elements.forEach(function(element) { element.style.display = displayProp; }) }); } function set(selector, value) { var elements = getAll(selector); elements.forEach(function(element) { element.innerHTML = value; element.value = value; }) } function clear(selector) { set(selector, ''); } function get(selector) { return document.querySelector(selector); } function getAll(selector) { return document.querySelectorAll(selector); } } </script> </body> </html>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js