I use webpack and TypeScript and it seems that d3-tip isn't able to work with webpack. I get error on mouseover events
"Uncaught TypeError: Cannot read property 'target' of null"
.
This error occurs because d3.event in the d3-tip module is null.
I include modules as follows:
const d3: any = require("d3");
d3.tip = require("d3-tip");
but I guess that d3 there and d3 in the d3-tip module are different and this is the source of problems, but I don't know how to solve it. In the d3-tip module we have:
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module with d3 as a dependency.
define(['d3'], factory)
} else if (typeof module === 'object' && module.exports) {
// CommonJS
var d3 = require('d3')
module.exports = factory(d3)
} else {
// Browser global.
root.d3.tip = factory(root.d3)
}
}(this, function (d3) {
...
And it compiles by webpack into
function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// d3.tip
// Copyright (c) 2013 Justin Palmer
//
// Tooltips for d3.js SVG visualizations
(function (root, factory) {
if (true) {
// AMD. Register as an anonymous module with d3 as a dependency.
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(465)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))
} else if (typeof module === 'object' && module.exports) {
// CommonJS
var d3 = require('d3')
module.exports = factory(d3)
} else {
// Browser global.
root.d3.tip = factory(root.d3)
}
}(this, function (d3) {
...
and it's obvious that AMD is using. If I could get the factory of d3-tip I would solve that problem.
pass the target element as the last parameter to tip.show()
var tip = require("d3-tip");
var tooltip = tip().html(d => d.value);
vis.call(tooltip)
vis.append("rect")
// ...
.on("mouseover", function() {
tooltip.show.apply(this, [...arguments, this]);
});
and d3-tip will pick it up and use as target. From source:
if (args[args.length - 1] instanceof SVGElement) target = args.pop()
another way:
.on("mouseover", function(d) {
tooltip.show(d, this);
});
I found the solution. As I think webpack produces many instances of each module when it's required. I've used single-module-instance-webpack-plugin
and it's solved my problem.
You also just need to initialize d3 in the fisrt time somewhere, it should be file like vendor.ts
where you include vendor libraries:
// D3 and third-party components
const d3: any = require("d3");
d3.tip = require("d3-tip");
For pure JS initialization would be easy.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With