How can I convert the following portion to be converted to one written with JS Class.
svg authored with d3 without JS Class
const svgns = 'http://www.w3.org/2000/svg';
const width = 1280;
const height = 600;
const main = d3.select('div')
.style('position', 'relative')
.append('svg')
.attr('xmlns', svgns)
.attr('viewBox', `0 0 ${width} ${height}`)
.attr('id', 'svg');
//listener
const listener = main
.append('rect')
.attr('class', 'vBoxRect')
.attr('width', `${width}`)
.attr('height', `${height}`)
.attr('fill', 'none')
.attr('stroke', 'red')
.style("pointer-events", "all");
const focusText = main
.append('g')
.attr('class', 'someTxt')
.append('text')
.attr("opacity", '0')
.attr('x', '100')
.attr('y', '100')
.html('I am a test text');
listener
.on('mouseover', function() {
focusText.attr("opacity", 1);
})
.on('mouseout', function() {
focusText.attr("opacity", 0);
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<body>
<div id="viz"></div>
</body>
<script src="min.js">
</script>
</html>
svg authored with d3 + JS Class and Constructor
const svgns = 'http://www.w3.org/2000/svg';
const width = 1280;
const height = 600;
class Base {
constructor(svgns, width, height) {
this.base = d3.select('div')
.style('position', 'relative')
.append('svg')
.attr('xmlns', svgns)
.attr('viewBox', `0 0 ${width} ${height}`)
.attr('id', 'svg');
//listener
this.listener = this.base
.append('rect')
.attr('class', 'vBoxRect')
.attr('width', `${width}`)
.attr('height', `${height}`)
.attr('fill', 'none')
.attr('stroke', 'red')
.style("pointer-events", "all");
this.focusText = this.base
.append('g')
.attr('class', 'someTxt')
.append('text')
.attr("opacity", '0')
.attr('x', '100')
.attr('y', '100')
.html('I am a test text');
this.listener
.on('mouseover', function() {
this.focusText.attr("opacity", 1);
})
.on('mouseout', function() {
this.focusText.attr("opacity", 0);
})
}
};
const svg = new Base(svgns, width, height);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<body>
<div id="viz"></div>
</body>
<script src="min.js">
</script>
</html>
The code is currently failing here
//without class
listener
.on('mouseover', function() {
focusText.attr("opacity", 1);
})
.on('mouseout', function() {
focusText.attr("opacity", 0);
})
//with Class
this.listener
.on('mouseover', function() {
this.focusText.attr("opacity", 1);
})
.on('mouseout', function() {
this.focusText.attr("opacity", 0);
})
The issue is that the event system sets its own value of this
when it calls your listener callback. In DOM events, the value of this
is typically the DOM element that triggered the event. There are a couple of ways to override that value of this
and specify your own.
To use the lexical value of this
, then use an arrow function instead of a regular function:
this.listener
.on('mouseover', () => {
this.focusText.attr("opacity", 1);
})
.on('mouseout', () => {
this.focusText.attr("opacity", 0);
})
Arrow functions ignore the value of this
that comes from how the function is called and they use the lexical value of this
which is the value of this
in the execution environment when the function is defined - which is what you want in your context.
Or, you can also use .bind()
(which creates a little stub functions that re-establishes the value of this
you want):
this.listener
.on('mouseover', function() {
this.focusText.attr("opacity", 1);
}.bind(this))
.on('mouseout', function() {
this.focusText.attr("opacity", 0);
}.bind(this))