score:1

Accepted answer

It sounds like you were almost there with adding the labels. You want to add them to the g element that holds the rect bars, and add them after the rects so they will appear on top of the bars, so:

  var element = g.append("g")
    .selectAll("g")
    .data(data)
    .enter().append("g")
    .attr("transform", function(d) {
      return "translate(0," + y0(d[yAxis]) + ")";
    });

  var rect = element.selectAll("rect")
    .data(function(d, i) {
      return keys.map(function(key) {
        return {
          key: key,
          value: d[key],
          index: key + "_" + i + "_" + d[yAxis]
        };
      });
    })
    .enter().append("rect")
    .attr("width", function (d) {
      return x(d.value);
    })

    [ etc. ]

  // add the text elements
  element.append('text')

The text element needs to be at the end of the bar, and the bar width is x(d.value) using the data bound to the rect elements. d.value translates to d.num in terms of the data you already have attached to the g elements, so we can set the x attribute as x(d.num). If the text-anchor is set to end, that will align the end of the text with the end of the bar; we want a little space between text and end of bar, so add in a small offset:

element.append('text')
 .attr('text-anchor', 'end')
 .attr('x', d => x(d.num) - 5)

The value to be shown in the bar is also going to be d.num, so we can add that:

element.append('text')
 .attr('text-anchor', 'end')
 .attr('x', d => x(d.num) - 5)
 .text(d => d.num )

If you run the code now, you'll find that the numbers are partially obscured by the bar above, so let's sort out the y offset. The bar width is y1.bandwidth(); to align the baseline of the text with the bottom of the bar, add

element.append('text')
 .attr('text-anchor', 'end')
 .attr('x', d => x(d.num) - 5)
 .attr('y', y1.bandwidth())
 .text(d => d.num )

Depending on what size you anticipate your users viewing the chart at, you might want to try to centre the text over the bar -- e.g. try

element.append('text')
 .attr('text-anchor', 'end')
 .attr('x', d => x(d.num) - 5)
 .attr('y', y1.bandwidth()/2)
 .attr('dy', '0.25em')
 .text(d => d.num )

Here's a working example:

$(window).on('resize', function(event) {
  $("#chart").width(window.innerWidth * 0.9);
  $("#chart").height(window.innerHeight);
});


function horizontalGroupBarChart(config) {
  function setReSizeEvent(data) {
    var resizeTimer;
    var interval = 500;
    window.removeEventListener('resize', function() {});
    window.addEventListener('resize', function(event) {
      if (resizeTimer !== false) {
        clearTimeout(resizeTimer);
      }
      resizeTimer = setTimeout(function() {
        $(data.mainDiv).empty();
        drawHorizontalGroupBarChartChart(data);
        clearTimeout(resizeTimer);
      }, interval);
    });
  }

  drawHorizontalGroupBarChartChart(config);
  setReSizeEvent(config);
}

function createhorizontalGroupBarChartLegend(mainDiv, columnsInfo, colorRange) {
  var z = d3.scaleOrdinal()
    .range(colorRange);
  var mainDivName = mainDiv.substr(1, mainDiv.length);
  $(mainDiv).before("<div id='Legend_" + mainDivName + "' class='pmd-card-body' style='margin-top:0; margin-bottom:0;text-align:center'></div>");
  var keys = Object.keys(columnsInfo);
  keys.forEach(function(d) {
    var cloloCode = z(d);
    $("#Legend_" + mainDivName).append("<span class='team-graph team1' style='display: inline-block; margin-right:10px;'>\
  			<span style='background:" + cloloCode + ";width: 10px;height: 10px;display: inline-block;vertical-align: middle;'>&nbsp;</span>\
  			<span style='padding-top: 0;font-family:Source Sans Pro, sans-serif;font-size: 13px;display: inline;'>" + columnsInfo[d] + " </span>\
  		</span>");
  });

}

function drawHorizontalGroupBarChartChart(config) {
  var data = config.data;
  var columnsInfo = config.columnsInfo;
  var xAxis = config.xAxis;
  var yAxis = config.yAxis;
  var colorRange = config.colorRange;
  var mainDiv = config.mainDiv;
  var mainDivName = mainDiv.substr(1, mainDiv.length);
  var label = config.label;
  var requireLegend = config.requireLegend;

  d3.select(mainDiv).append("svg").attr("width", $(mainDiv).width()).attr("height", $(mainDiv).height() * 0.80).attr("class", "mainSVG")
  var svg = d3.select(mainDiv + " svg"),
    margin = {
      top: 20,
      right: 20,
      bottom: 40,
      left: 40
    },
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom;


  var g = svg.append("g").attr("transform", "translate(" + (margin.left * 2.3) + "," + margin.top + ")");

  if (requireLegend != null && requireLegend != undefined && requireLegend != false) {
    $("#Legend_" + mainDivName).remove();
    createhorizontalGroupBarChartLegend(mainDiv, columnsInfo, colorRange);
  }

  $(".mainSVG").attr("transform", "translate(5,10)")


  var y0 = d3.scaleBand()
    .rangeRound([height, 0])
    .paddingInner(0.1);


  var y1 = d3.scaleBand()
    .padding(0.05);


  var x = d3.scaleLinear()
    .rangeRound([0, width - margin.left]);


  var z = d3.scaleOrdinal()
    .range(colorRange);

  var keys = Object.keys(columnsInfo);
  y0.domain(data.map(function(d) {
    return d[yAxis];
  }));
  y1.domain(keys).rangeRound([0, y0.bandwidth()]);
  x.domain([0, d3.max(data, function(d) {
    return d3.max(keys, function(key) {
      return d[key];
    });
  })]).nice();
  var maxTicks = d3.max(data, function(d) {
    return d3.max(keys, function(key) {
      return d[key];
    });
  });
  var element = g.append("g")
    .selectAll("g")
    .data(data)
    .enter().append("g")
    .attr("transform", function(d) {
      return "translate(0," + y0(d[yAxis]) + ")";
    });
     
  var rect = element.selectAll("rect")
    .data(function(d, i) {
      return keys.map(function(key) {
        return {
          key: key,
          value: d[key],
          index: key + "_" + i + "_" + d[yAxis]
        };
      });
    })
    .enter().append("rect")
    .attr("y", function(d) {
      return y1(d.key);
    })
    .attr("width", function(d) {
      return x(d.value);
    })
    .attr("data-index", function(d, i) {
      return d.index;
    })
    .attr("height", y1.bandwidth())
    .attr("fill", function(d) {
      return z(d.key);
    })


   element.append('text')
     .attr('x', d => x(d.num) - 5)
     .attr('y', y1.bandwidth()/2)
     .attr('dy', '0.35em')
     .attr('text-anchor', 'end')
     .text(d => d.num )

  var datax = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];


  var tScale = d3.scaleLinear()
    .rangeRound([0, width - margin.left]);


  tScale.domain(d3.extent(datax)).nice();





  //CBT:add tooltips
  var self = {};
  self.svg = svg;
  self.cssPrefix = "horgroupBar0_";
  self.data = data;
  self.keys = keys;
  self.height = height;
  self.width = width;
  self.label = label;
  self.yAxis = yAxis;
  self.xAxis = xAxis;
  horBarTooltip.addTooltips(self);

  rect.on("mouseover", function() {
    var currentEl = d3.select(this);
    var index = currentEl.attr("data-index");
    horBarTooltip.showTooltip(self, index);
  });

  rect.on("mouseout", function() {
    var currentEl = d3.select(this);
    var index = currentEl.attr("data-index");
    horBarTooltip.hideTooltip(self, index);
  });

  rect.on("mousemove", function() {
    horBarTooltip.moveTooltip(self);
  });


  g.append("g")
    .attr("class", "axis")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(x).ticks(maxTicks))
    .append("text")
    .attr("x", width / 2)
    .attr("y", margin.bottom * 0.7)
    .attr("dx", "0.32em")
    .attr("fill", "#000")
    .attr("font-weight", "bold")

    .attr("text-anchor", "start")


  g.append("g")
    .attr("class", "axis")
    .call(d3.axisLeft(y0).ticks(null, "s"))
    .append("text")
    .attr("x", height * 0.4 * -1)
    .attr("y", margin.left * 0.8 * -1) //y(y.ticks().pop()) + 0.5)
    .attr("dy", "0.71em")
    .attr("fill", "#00338D")

    .attr("font-weight", "bold")
  // .attr("text-anchor", "start")


}
var helpers = {
  getDimensions: function(id) {
    var el = document.getElementById(id);
    var w = 0,
      h = 0;
    if (el) {
      var dimensions = el.getBBox();
      w = dimensions.width;
      h = dimensions.height;
    } else {
      console.log("error: getDimensions() " + id + " not found.");
    }
    return {
      w: w,
      h: h
    };
  }
}
var horBarTooltip = {
  addTooltips: function(pie) {
    var keys = pie.keys;
    // group the label groups (label, percentage, value) into a single element for simpler positioning
    var element = pie.svg.append("g")
      .selectAll("g")
      .data(pie.data)
      .enter().append("g")
      .attr("class", function(d, i) {
        return pie.cssPrefix + "tooltips" + "_" + i
      });

    tooltips = element.selectAll("g")
      .data(function(d, i) {
        return keys.map(function(key) {
          return {
            key: key,
            value: d[key],
            index: key + "_" + i + "_" + d[pie.yAxis]
          };
        });
      })
      .enter()
      .append("g")
      .attr("class", pie.cssPrefix + "tooltip")
      .attr("id", function(d, i) {
        return pie.cssPrefix + "tooltip" + d.index;
      })
      .style("opacity", 0)
      .append("rect")
      .attr("rx", 2)
      .attr("ry", 2)
      .attr("x", -2)
      .attr("opacity", 0.71)
      .style("fill", "#000000");

    element.selectAll("g")
      .data(function(d, i) {
        return keys.map(function(key) {
          return {
            key: key,
            value: d[key],
            index: key + "_" + i + "_" + d[pie.yAxis]
          };
        });
      })
      .append("text")
      .attr("fill", function(d) {
        return "#efefef"
      })
      .style("font-size", function(d) {
        return 10;
      })
      .style("font-family", function(d) {
        return "arial";
      })
      .text(function(d, i) {
        var caption = "" + pie.label.xAxis + ":{value}";

        return horBarTooltip.replacePlaceholders(pie, caption, i, {
          value: d.value,
        });
      });

    element.selectAll("g rect")
      .attr("width", function(d, i) {
        var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index);
        return dims.w + (2 * 4);
      })
      .attr("height", function(d, i) {
        var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index);
        return dims.h + (2 * 4);
      })
      .attr("y", function(d, i) {
        var dims = helpers.getDimensions(pie.cssPrefix + "tooltip" + d.index);
        return -(dims.h / 2) + 1;
      });
  },

  showTooltip: function(pie, index) {
    var fadeInSpeed = 250;
    if (horBarTooltip.currentTooltip === index) {
      fadeInSpeed = 1;
    }

    horBarTooltip.currentTooltip = index;
    d3.select("#" + pie.cssPrefix + "tooltip" + index)
      .transition()
      .duration(fadeInSpeed)
      .style("opacity", function() {
        return 1;
      });

    horBarTooltip.moveTooltip(pie);
  },

  moveTooltip: function(pie) {
    d3.selectAll("#" + pie.cssPrefix + "tooltip" + horBarTooltip.currentTooltip)
      .attr("transform", function(d) {
        var mouseCoords = d3.mouse(this.parentNode);
        var x = mouseCoords[0] + 4 + 2;
        var y = mouseCoords[1] - (2 * 4) - 2;
        return "translate(" + x + "," + y + ")";
      });
  },

  hideTooltip: function(pie, index) {
    d3.select("#" + pie.cssPrefix + "tooltip" + index)
      .style("opacity", function() {
        return 0;
      });

    // move the tooltip offscreen. This ensures that when the user next mouseovers the segment the hidden
    // element won't interfere
    d3.select("#" + pie.cssPrefix + "tooltip" + horBarTooltip.currentTooltip)
      .attr("transform", function(d, i) {
        // klutzy, but it accounts for tooltip padding which could push it onscreen
        var x = pie.width + 1000;
        var y = pie.height + 1000;
        return "translate(" + x + "," + y + ")";
      });
  },

  replacePlaceholders: function(pie, str, index, replacements) {
    var replacer = function() {
      return function(match) {
        var placeholder = arguments[1];
        if (replacements.hasOwnProperty(placeholder)) {
          return replacements[arguments[1]];
        } else {
          return arguments[0];
        }
      };
    };
    return str.replace(/\{(\w+)\}/g, replacer(replacements));
  }
};



var groupChartData = [{
  "num": 1,
  "over": "Singapore"
}, {
  "num": 1.3,
  "over": "The Netherlands"
}, {
  "num": 2,
  "over": "United Kingdom"
}, {
  "num": 2.4,
  "over": "United States"
}, {
  "num": 2.6,
  "over": "New Zealand"
}, {
  "num": 2.8,
  "over": "Sweden"
}, {
  "num": 3,
  "over": "Canada"
}, {
  "num": 3,
  "over": "UAE"
}, {
  "num": 4,
  "over": "Australia"
}, {
  "num": 4.4,
  "over": "France"
}, {
  "num": 5,
  "over": "South Korea"
}, {
  "num": 5.2,
  "over": "Germany"
}, {
  "num": 5.5,
  "over": "Austria"
}, {
  "num": 6,
  "over": "Austria"
}, {
  "num": 7,
  "over": "Brazil"
}, {
  "num": 7,
  "over": "China"
}, {
  "num": 8,
  "over": "Japan"
}, {
  "num": 10,
  "over": "Russia"
}, {
  "num": 11,
  "over": "Mexico"
}, {
  "num": 12,
  "over": "India"
}, ];
var columnsInfo = {
  "num": "<span class='mainTitle KPMGWeb-ExtraLight'>Technology & innovation pillar: score by country</span>"
};

$("#chart").empty();
var barChartConfig = {
  mainDiv: "#chart",
  colorRange: ["#0091DA", "#6D2077"],
  data: groupChartData,
  columnsInfo: columnsInfo,
  xAxis: "runs",
  yAxis: "over",
  label: {
    xAxis: "",
    yAxis: ""
  },
  requireLegend: true
};
var groupChart = new horizontalGroupBarChart(barChartConfig);
.mainTitle {
  font-size: 3em;
}
svg text {
  font-size: 10px;
  font-family: sans-serif;
}
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="chart" style="width: 800;height: 600">

By the way, I noticed that there seem to be two bars on the same line for Austria - not sure if that is deliberate or not.


Related Query

More Query from same tag