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

Typegoose's representation of modifying data

Recently, I delved into the world of NestJS and kickstarted a sample project. To integrate MongoDB seamlessly, I opted for Typegoose. A useful online tutorial () caught my eye, illustrating how to employ abstractions with base typegoose models. Hence, my ...

What is the best way to generate a div with a dynamically changing variable as its ID?

Creating a quiz where the user can choose how many questions to answer. A function is used to generate individual question divs based on the user's input. Each question displays a Chinese character, and the user must select the correct translation. ...

Utilize the capabilities of the Dropbox Core API in Javascript to effortlessly transfer and store files on

I am currently developing a chrome-extension that has the ability to upload files to the user's Dropbox folder. I have implemented AJAX requests in my code to handle file uploads, and it is working fine for text-based file extensions such as .txt, .js ...

What causes compatibility issues between JEST and import statements in NEXTJS?

Just starting out with unit testing in JavaScript and I'm attempting to create a unit test for a Next JS project. However, when running the test, I encountered the following error: Code: import {isBase64} from '../../service/base64-service&a ...

A guide on utilizing the useEffect hook to dynamically update a button icon when hovering over it in a React application

Is it possible to change the icon on a button when hovering using useEffect? <Button style={{ backgroundColor: "transparent" }} type="primary" icon={<img src={plusCart} />} onCl ...

How to incorporate markdown files as strings in Next.js

Is there a way to bring in markdown files as strings in Next.js for use on both the client and server sides? ...

Close pop-up upon successful AJAX response in ASP.NET MVC

I have a modal in my view that allows me to create a new record in the database. The view for this functionality is contained within a partial view. Below is the code for the view: <script src="~/Scripts/jquery-3.1.1.js"></script> To han ...

Using Javascript, incorporate a string variable into the response

Currently, I'm working with a node.js program and I've come across this response: 'Content-Disposition': 'attachment; filename="tts.mp3"' Instead of using "tts.mp3," I have a variable named textToConvert. How can I modify it ...

Add a preventDefault event listener to a submit button that triggers a specific function

$(function() { $('#login').submit(function(e){ preventSubmission(); e.preventDefault(); }); }); function preventSubmission() { $('#btnLogin').attr('disabled','disabled'); $("#btnLogi ...

What is causing the fs.readFile function to give back undefined instead of the expected result?

/** * A function to determine the cost of an employee from a specific data file * @param {string} filePath - the path to the employee data file * @returns {{name: string, cost: number}} - the name and cost of the employee */ function calculateEmployee ...

Can express-handlebars tags be utilized within an HTML script tag when on the client side?

For a while now, I've been facing a challenge. I'm in the process of building an application with express-handlebars and so far everything is going smoothly. The data that needs to be displayed on the webpages looks good thanks to the Helper func ...

Tips for revealing a position: absolute div that is currently hidden with display: none styling

Below is the code for a div element that I want to temporarily hide using JavaScript: <div id="mydiv" style="position: absolute; top: 60px; left:5px; right:25px; bottom:10px;"> </div> After hiding it with display:none in my ...

Angular 4's Mddialog experiencing intermittent display problem

While using MDDialog in my Angular app, I've encountered a couple of issues. Whenever a user clicks on the div, flickering occurs. Additionally, if the user then clicks on one of the buttons, the afterclose event is not triggered. Can anyone provide ...

Sending an Ajax POST request from a Node.js server

I am running a Node.js server with Socket.IO that communicates with a Python server using Django. I am looking to make a POST request from the Node.js server to the Django server on a specific method without utilizing any jQuery functions due to their depe ...

Which specific technological platform or framework would be most suitable for constructing a similar project?

https://i.stack.imgur.com/LL1g9.png Looking at the image provided, my goal is to allow users to navigate between pages on the Home page without having to refresh the entire browser window. I believe this can be achieved using Ajax technology, am I correct ...

Is utilizing unregistered HTML elements for semantic purposes considered poor practice?

When it comes to styling and semantic purposes, I am considering using unregistered web components. This means utilizing tags like <t-card></t-card> without registering them with customElements.define. Surprisingly, the browser and stylesheets ...

Encountering an abundance of concurrent requests using NodeJS and request-promise

I am currently working on a NodeJS project that involves a large array of about 9000 elements containing URLs. These URLs need to be requested using the "request-promise" package. However, making 9000 concurrent GET requests to the same website from the sa ...

Looking to introduce Vue.js into an established SSR website?

Can Vue be used to create components that can be instantiated onto custom tags rendered by a PHP application, similar to "custom elements light"? While mounting the Vue instance onto the page root element seems to work, it appears that Vue uses the entire ...

Slide feature in Drupal Views allows you to create dynamic

This is the design I currently have: https://i.stack.imgur.com/P6spc.jpg For example, when you click on the "Structure" header, it opens up the contents and shows an image. I have created a content type and installed Views. I have separated the image, h ...

The percentage height setting for a div is not functioning properly, but setting the height in pixels or viewport

Within a dialog box body, I am attempting to display a table and have applied a CSS class to the wrapping div. When specifying the height in pixels or viewport height units, it works as expected. However, when using a percentage like 50%, the height of the ...