Inquiry about how TypeScript handles object property references when passed into functions

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!

Answer №1

When working on your code, you may encounter a situation where a reference to the parent is needed. One way to tackle this issue is by utilizing arrow functions. The traditional function declaration (function () {}) will generate its own this, requiring the retention of the parent this like with parentObject.

In contrast, arrow functions (using the => syntax) do not set their own this. This implies that the this within an arrow function points to the parent's this, eliminating the necessity for a separate reference; you can access this directly.

You can also convert the drag handler into an arrow function for simplicity.

The following code reflects these modifications. Please review it as there might be overlooked details:

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', () =>
     this.moveHandle(this.handleTop, this.handleTopY, d3.event.dy, this.sliderTopBound, this.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', () => this.moveHandle(this.handleBottomY, this.handleTopY, d3.event.y, this.sliderBottomBound);
);

private moveHandle = (currentPosition: number, topBound: number, increment: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)
        this.attr("transform", "translate(0," + currentPosition + ")");
    }

}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Dynamic options can now be accessed and modified using newly computed getters and setters

When using Vuex with Vue components, handling static fields that are editable is easily done through computed properties: computed: { text: { get() { return ... }, set(value) { this.$store.commit... }, }, }, <input type ...

JavaScript: function that operates asynchronously

I am new to JavaScript and encountered some issues with async functions. I have a function that fetches data from MongoDB by creating a promise, but it returns a collection with an empty object. async function getRating(item, applicant) { let arr = await ...

Tips for eliminating additional white space within a bootstrap row

I'm having trouble removing the extra space on my website while using Bootstrap 5. I've tried various Bootstrap classes like pr-0, mr-0, p-auto, m-auto but none of them seem to work. I also attempted using CSS margin-right: 0; but that didn' ...

How to modify a specific property of an array object in JavaScript

I have an array of objects that looks like this: [ { number: 1, name: "A" }, { number: 2, name: "e", }, { number: 3, name: "EE", } ] I am looking for a way to insert an object into the array at a specific position and ...

Using a For Loop in VueJS with TypeScript for an array of objects

I'm currently working on a for loop within an object array. Below is the code snippet I am working with: private async rightsOverview() { let item: any[] = []; const prod = await fetchFromApi<ProductionBaseType>(`/productions/${id ...

access older siblings, accordion animation

I need assistance achieving an accordion effect on a DIV when hovering over it. The right side of the accordion is functioning properly, but I am facing issues with the left side. You can view my code on jsFiddle Could someone please provide guidance on ...

The post method is functioning properly in browsers such as Firefox, Internet Explorer, and Chrome; however, it is not working in the Edge browser

I am encountering an issue with a post method in the Edge browser. Even though I am able to receive responses for the same request in other browsers like Internet Explorer, Chrome, and Firefox, Edge seems to be not responding at all. Despite conducting a s ...

React checkbox displaying incorrect render

I'm currently working on developing a React component that acts as a tile representation. This particular component consists of a div containing a label and a checkbox. The issue I'm facing is that I can click anywhere on the component to trigg ...

Angular is having trouble with binding

What seems to be the issue in this code snippet? JSFiddle. function SecondCtrl($scope, Data) { $scope.data = Data; $scope.reversedMessage = function(message) { return message.split("").reverse().join(""); }; } ...

Angular ngx-translate not displaying image

My Angular application is utilizing ngx-translate to support multiple languages. I am trying to dynamically change an image based on the language selected by the user. However, I am facing difficulty in updating the image when a language is clicked. The ap ...

Is there a way to verify the presence of a service worker on a specific URL?

Is there a way for me to determine if external websites have a 'service-worker' or not? Here is what I think could work: Extract all the JavaScript files from the given URL Look for the string 'sw.js' (I am not entirely sure how to ...

Determine if the given text matches the name of the individual associated with a specific identification number

Struggling to create a validation system for two sets of fields. There are 6 inputs in total, with 3 designated for entering a name and the other 3 for an ID number. The validation rule is that if an input with name="RE_SignedByID" contains a value, then c ...

What causes the cleanup function in React hooks to be triggered upon reopening a previously closed tab?

There seems to be an issue with closing a tab and then undoing that action. This causes all the cleanup functions in the component to execute, resulting in the abortion of new fetches needed to load the component. This behavior is observed only on Safari ...

When an `angularjs select` is used with a filter, the first line may appear empty at first. However

I'm feeling a bit confused about why my ng-options is once again giving me an empty line with a filter applied. Could you please take a look at this plunker to see the issue? The goal is to show an org chart in a dropdown list that is based on a tre ...

An element featuring a background color is vertically aligned in the middle of its parent container

Struggling to achieve a seemingly simple task, but coming up short on finding a solution. The goal is to have a background-color that aligns vertically in the middle of the first and last images in a stack of images. It may sound more complicated than it a ...

Guide to submitting a form with an image upon pressing a button

I am working with the following HTML structure: <form id="form" enctype="multipart/form-data"> <div class="row"> <div class="col"> <div class="mb-3"> ...

How to display a name in an iframe using React

When I retrieve the movie name in React, it appears correctly as {movie.name} / {movie.enname} ({years}) . However, when I try to display this name within an iframe window at https://example.com/movie/{movie.name}, it does not show up properly. Here is th ...

Malfunction in triggering events within an Ajax Magnific popup feature

I'm trying to load a page within a magnific popup using ajax: $("#operator").magnificPopup({ delegate: 'a.edit', mainClass: 'mfp-fade', closeBtnInside: true, removalDelay: 300, closeOnContentClick: false, t ...

Is the iCheck feature designed to block all parent click events?

I've developed an "interaction tracker" system that collects anonymous data on clicked page elements. By implementing an event listener for clicks on the body, I can track interactions with any element on my site successfully. However, interestingly ...

What factors does mongo consider when serializing an object?

I recently started working with BigNumbers from the bignumber.js package As I delve into Mongo, I find myself pondering how Mongo manages to serialize objects correctly, such as the BigNumbers. In my case, I have encountered a puzzling situation where two ...