As a newcomer to TypeScript, I am exploring the creation of a range slider with dual handles using D3.js.
I have developed a simple class for managing the slider objects:
export class VerticalRangeSlider{
private sliderContainer: d3.Selection<SVGGElement, any, HTMLDivElement, any>;
private scale: d3.ScaleLinear<number, number>;
private sliderTopBound: number;
private sliderBottomBound: number;
private handleTopY: number;
private handleBottomY: number;
private handleTop: d3.Selection<SVGCircleElement, any, HTMLDivElement, any>;
private handleBottom: d3.Selection<SVGCircleElement, any, HTMLDivElement, any>;
constructor(slContainer: d3.Selection<SVGGElement, any, HTMLDivElement, any>, scl: d3.ScaleLinear<number, number>){
this.sliderContainer = slContainer;
this.scale = scl;
this.sliderTopBound = 0;
this.sliderBottomBound = scl.range()[0];
this.handleTopY = this.sliderTopBound;
this.handleBottomY = this.sliderBottomBound;
this.initSlider();
}
The initiation of the two handles takes place within the initSlider method. An anonymous function is utilized to manage drag events by calling the moveHandle method for redrawing the circles.
To address the change in context that occurs with 'this.' in an anonymous function, a variable called parentObject is declared within initSlider to store the reference to the parent object: var parentObject = this;
.
this.handleTop = this.sliderContainer.insert("circle", ".track-overlay")
.attr("class", "handle")
.attr("r", 9)
.attr("cy", this.handleTopY)
.attr("desc", this.handleTopY)
.call(d3.drag()
.on('drag', function(){
parentObject.moveHandle(parentObject.handleTop, parentObject.handleTopY, d3.event.dy, parentObject.sliderTopBound, parentObject.handleBottomY);
}));
this.handleBottom = this.sliderContainer.insert("circle", ".track-overlay")
.attr("class", "handle")
.attr("r", 9)
.attr("cy", this.handleBottomY)
.attr("desc", this.handleBottomY)
.call(d3.drag()
.on('drag', function(){
parentObject.moveHandle(parentObject.handleBottom, parentObject.handleBottomY, d3.event.dy, parentObject.handleTopY, parentObject.sliderBottomBound);
}));
An issue was encountered with the moveHandle method:
private moveHandle(handle: d3.Selection<SVGCircleElement, any, HTMLDivElement, any>, currentPosition: number, increment: number, topBound: number, bottomBound:number): void {
var legalIncrement: number;
//upward movement
if(Math.sign(increment) === -1){
legalIncrement = increment <= (currentPosition - topBound) ? increment : (currentPosition - topBound);
console.log("allowed increment: "+legalIncrement);
}
//downward movement
else {
legalIncrement = increment <= (bottomBound - currentPosition) ? increment : (bottomBound - currentPosition);
console.log("allowed increment: "+legalIncrement);
}
if(legalIncrement !== 0){
currentPosition = (currentPosition + legalIncrement)
handle.attr("transform", "translate(0," + currentPosition + ")");
}
}
Upon attempting to drag the circles, they momentarily rendered correctly but swiftly reverted to their original positions.
However, everything operated smoothly when directly passing a pointer to the parent object into moveHandle:
parentObject: VerticalRangeSlider): void {
parentObject.handleBottomY = parentObject.handleBottomY + increment; parentObject.handleBottom.attr("cy", parentObject.handleBottomY);
Initially, I assumed that I was passing pointers to the properties of the parent object into my method but it appears that new objects are being created instead.
I would greatly appreciate any insights into this behavior (as well as any suggestions for enhancing the structure of my JS/TS code).
Thank you!