data update problem with express and prisma

I have a prisma model, updateController, and testing from postman as follows:
(https://i.stack.imgur.com/PQEg2.png)(https://i.stack.imgur.com/Wsqy8.png)(https://i.stack.imgur.com/7CgwA.png)(https://i.stack.imgur.com/0qnca.png)

in postman, it can be seen that there are two subMenus added, namely there is a test2 along with the name and url and there is also a test3. But the result becomes all tes3. How to solve it?

Concurrency Issue in TypeORM – multiple records created when only one should be

I’ve got the following code:

for (const vehicleDefinition of vehicleDefinitions) {
  if (vehicleDefinition) {
    const existingDefinition = await getExistingDefinition(
      vehicleService,
      vehicleDefinition.make,
      vehicleDefinition.model,
      vehicleDefinition.year
    );

    if (!existingDefinition) {
      await createNewDefinitions(
        entityManager,
        vehicleService,
       vehicleDefinition
      );
    }
  }
}

const getExistingDefinition = async (vehicleService, make, model, year) => {
  return vehicleService.retrieveDefinitionByCriteria(make, model, year);
};

const createNewDefinitions = async (
  entityManager,
  vehicleService,
  newVehicleDefinitions
) => {
  if (newVehicleDefinitions.length === 0) {
    return;
  }

  await entityManager.transaction(async (manager) => {
    const promises = newVehicleDefinitions.map(async (newVehicleDefinition) => {
      await vehicleService
        .withTransaction(manager)
        .createDefinition(newVehicleDefinition);
    });
    await Promise.all(promises);
  });
};

However for some reason, sometimes new transactions are happening, before old ones complete. This is using TypeORM.

I am trying to wrap everything in async/await, but with no luck, it STILL manages to start a new transaction before an old one is finished. Essentially multiple instances of createNewDefinitions are being called, before the transaction in one of them is complete.

What am I doing wrong to prevent concurrency issues?

Typeerror: Cannot read properties of null (reading ‘style’) [duplicate]

I am using JavaScript and the below line gives me a type error.

if (btnvar1.style.color=="white")

Error

Uncaught TypeError: Cannot read properties of null (reading 'style')

HTML

I am using Tailwind as CSS and Font Awesome for icons.

<div>
<button onclick="toggle1()" id="lovebtn1" class="btn bg-transparent border-none p-0 text-white hover:cursor-pointer"><i class="fa-solid fa-heart text-3xl"></i></button>
</div>

JavaScript

var btnvar1=document.getElementById("lovebtn1");

function toggle1(){
    if (btnvar1.style.color=="white"){
        btnvar1.style.color="red";
    }
    else{
        btnvar1.style.color="white";
    }
}

I was trying to make a love react button like Instagram but without the animation using JavaScript.And I was trying to set a condition to change the color of the icon.

ajax call to refresh partial view on detail page not working after edit action from modal

I have a partial in a Detail view. I use a modal with the edit action to update the child relationship of the record in the Detail view. When I hit Save on the edit modal, the record updates, and I want the partial that holds the child relationship data to refresh, instead of the the whole page refreshing.

So far, I’ve been able to get the edit modal to work with ajax. But my script for refreshing the partial isn’t working. I put a breakpoint in the controller action called by the ajax script but when clicking the Save button on the modal edit form the breakpoint never fires. I don’t know why the controller action isn’t being called by the ajax script.

The Detail view is a bootstrap tabbed view. The tab I am working on is the Relationships tab. When I use the Edit modal to update a relationship, I want the Relationship tab to refresh, not the whole page.

I should note that the Edit modal is a view component based on the Relationship model, and the Detail view is based on the Contact model. This part works. I put the line return NoContent(); at the end of the Relationship controller POST;Edit action. I used the click event of the Save button to handle closing the Edit modal and refreshing the partial in the Detail view.

script


function refreshRelationships() {
    closeEdit(); // closes the Edit Modal
    req = $.ajax({
        url: "/Contacts/RefreshRelationshipsTab/",
        method: "GET",
        data: {},
        contentType: "application/json; charset=utf-8",
        dataType: "html",
        success: function (response) {
            $("#nav-relationship").html("");
            $("#nav-relationship").html(response);
        }
    });
};

Partial View _RelationshipsTab

@if (@Model.RelationshipsAsParent.Count > 0)
{
    <table class="table table-bordered">
        <thead>
            <tr>
                <th scope="col">Name</th>
                <th scope="col">Type</th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @for (int j = 0; j < Model.RelationshipsAsParent.Count; j++)
            {
                <tr>
                    <td>@Model.RelationshipsAsParent[j].Child.FullName</td>
                    <td>@Model.RelationshipsAsParent[j].RelationshipType.Type</td>
                    <td>
                        <a onclick="RelationshipsEdit(@Model.RelationshipsAsParent[j].Id)" class="btn btn-sm btn-primary">
                            Edit
                        </a> |
                        <a asp-controller="Relationships" asp-action="Delete" asp-route-id="@Model.RelationshipsAsParent[j].Id">Delete</a>
                    </td>
                </tr>

            }
        </tbody>
    </table>
}
else
{
    <h3>No Relationships</h3>
}

Contacts Controller

public async Task<ActionResult> RefreshRelationshipsTab()
{
    return PartialView("_RelationshipsTab");
}

Button in Edit Modal form

<button onclick="refreshRelationships()" type="submit" value="Save" class="btn btn-primary">Save</button>

Partial View div in Details view

<div class="tab-pane fade" id="nav-relationship" role="tabpanel" aria-labelledby="nav-relationship-tab">
    <partial name="_RelationshipsTab"/>
</div>

Thanks in advance for any help.

Why the variable change from string to undefined in JavaScript?

I have a JavaScript file that has a variable taken from another JavaScript file but at the start it has a string value then later in the code it is undefined.

here is the code:

const aws = require('aws-sdk');
   const multer = require('multer');
   const multerS3 = require('multer-s3');
   const dotenv = require('dotenv');
   dotenv.config();

    const request = require('../server');
    let test = request.token1;
    
    
   aws.config.update({
    secretAccessKey: process.env.SECRET_ACCESS_KEY,
    accessKeyId: process.env.ACCESS_KEY_ID,
    region: 'ap-south-1'
   });

   const s3 = new aws.S3();
   const upload = multer({
   storage: multerS3({
    acl: 'public-read',
    s3,
    bucket: 'angular-upload-files',
    key: function(req, file, cb) {
      req.file = file.originalname;
      //include the folder created before uploading files...
      cb(null, test+"/" + file.originalname);
     }
    })
   });

   module.exports = upload;

so the variable is called test, when I get the variable it has a value of “one” as a string (tried to console.log) but when I use it at the end as a key for aws s3 bucket and tried to (console.log) it gives me (undefined, one) so the data will be save under folder called “undefined” rather than “one”.

Merge or combine multiple api responses as one object as they keep coming Javascript

I am making multiple api calls and the api response comes back in chunks of objects.
the response from first api call is like:

   {
        "recordone": {
            "abc": [
                {
                    "prop1": "value1"
                }
            ]
        },
        "recordtwo": {
            "ghi": [
                {
                    "prop2": "value2"
                }
            ],
            "jkl": []
        },
        "recordthree": {
            "mno": [
                {
                    "prop3": "value3"

                }
            ]
        }
    }

The response from 2nd api call like:

  {
        "recordone": {
            "abc": [
                {
                    "prop4": "value4"
                }
            ]
           
        },
        "recordtwo": {
            "ghi": [
                {
                    "prop5": "value5"
                }
            ]
        },
        "recordthree": {
            "mno": [
                {
                    "prop6": "value6"
                }
            ]
        }
    }

And so on.

I want to keep combining/merging these responses as they keep coming/loading. The expected result should be as follows:

 {
        "recordone": {
            "abc": [
                {
                    "prop1": "value1"
                },
                {
                    "prop4": "value4"
                }
            ]
        },
        "recordtwo": {
            "ghi": [
                {
                    "prop2": "value2"
                },
                {
                    "prop5": "value5"
                }
            ]
        },
        "recordthree": {
            "mno": [
                {
                    "prop3": "value3"

                },
                {
                    "prop6": "value6"
                }
            ]
        }
    }

How can we do it in Typescript? How can update/combine the responses as the keep loading?

Prevent user from typing more than a single decimal, and prevent user from typing more than 2 numbers after decimal

I’ve currently got this JS code attached that validates for numbers and dots in an input, but I want to also prevent user from typing in more than one dot for decimal, and not more than two other numbers after the dot.

How can I do this?

Thank you

function validate(evt) {
    var theEvent = evt || window.event;
    // Handle paste
    if (theEvent.type === 'paste') {
        key = event.clipboardData.getData('text/plain');
    } else {
    // Handle key press
        var key = theEvent.keyCode || theEvent.which;
        key = String.fromCharCode(key);
    }
    var regex = /[0-9]|./;
    if( !regex.test(key) ) {
      theEvent.returnValue = false;
      if(theEvent.preventDefault) theEvent.preventDefault();
    }
  }
        <input placeholder="Enter amount..." maxlength="5" onkeypress='validate(event)'>

npm reports an error when installing hexo

Use npm to install hexo and execute this command “npm install hexo-deployer-git –save”. The error message is: npm ERR! Cannot read properties of null (reading ‘matches’),

enter image description here

I tried clearing the cache: “npm cache clean –force”, but it still didn’t work

Converting Next.js – Javascript code to Typescript

I am in the process of converting my Javascript code to TypeScript for a web application I am making on top of Next.Js

The following is my converted code:

'use client'

import React, { useState, ChangeEvent, FormEvent } from 'react';

interface FormData {
  email: string;
}

interface MessageStatus {
  message?: string; 
  success?: boolean;
}

interface ErrorResponse {
  error: string;
}

const EmailSubmissionComponent: React.FC = () => {
  const [formData, setFormData] = useState<FormData>({email: ''});
  const [messageStatus, setMessageStatus] = useState<MessageStatus>({});

  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    setFormData({ ...formData, [event.target.name]: event.target.value });
  };

  const handleSumbit = async (event: FormEvent<HTMLFormElement>) => {
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; //Should remove if not testing locally. 
    event.preventDefault();

    if (formData.email != "") {
      try {
        const response = await fetch('REDACTED', { //Change for production
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(formData)
        });
  
        console.log('Response:', response); // Log response
        if (response.ok) {
          const responseBody = await response.json();
          // console.log('Response Body:', responseBody);
          setMessageStatus({message: "", success: true})
          // console.log('Form submitted successfully');
        } else {
          const errorResponse: ErrorResponse = await response.json(); 
          setMessageStatus({message:errorResponse.error, success:false})
          console.error('Form submission failed');
        }
      } catch (error :any) {
        setMessageStatus({message: "Network Error: Please try again shortly", success: false})
        console.log("Printing Errors:", error.message);
  
      }
    }
  };

  return (
    <div className="flex flex-col sm:w-[600px]">
      <form onSubmit={handleSubmit} className="flex flex-col sm:flex-row">
        <input type="email" name="email" value={formData.email} onChange={handleChange} placeholder="E-mail Address" className="sm:flex items-stretch flex-grow focus:outline-none block rounded-lg sm:rounded-none sm:rounded-l-lg pl-4 py-2"></input>
        
        <button type="submit" className="sm:mt-0 sm:w-auto sm:-ml-2 py-2 px-2 rounded-lg font-medium text-white focus:outline-none bg-logo-blue">
          Stay in the Loop
        </button>
        
      </form>
      {messageStatus.message && <div className="border-2 rounded border-red-700 bg-red-300 py-2 px-2 mt-1">
        {messageStatus.message}</div>}
      {messageStatus.success && <div className="border-2 rounded border-green-700 bg-green-300 py-2 px-2 mt-1">
        Congrats! You have signed up. Stay Tuned!</div>}
    </div>
  );

}

export default EmailSubmissionComponent;

I have addressed all type related error.
I am still getting syntax errors around the HTML code I have written in the return block.

Through some research I have come across solutions that relate to the tsconfig.json file and setting the jsx property to be “react”. However the error persists.

Any guidance on why I am getting a syntax error at my HTML code when doing this conversation would be much appreciated.

Thanks

Changing the fill/unfill state of an ellipse based on a click of a button while keeping track if the user selects another color

I’m working on a drawing app project for University, I am only allowed to use vanilla Javascript with p5.js. I have different constructors in separate files, for example the colourPalette where the user selects a color (and the fill of that color is declared inside this constructor), another circleTool constructor where I’m creating an ellipse and when this tool is selected, a button appears that can toggle between filled/unfilled.

Now my issue is that the toggle button works at first with no issues but when I’m selecting a new color while the button is “not filled”, the fill inside colourPalette is enabled again but my button does not update so I came up with the idea of calling the populateOptions() function from the circleTool every time I change the color because this will recreate the button in the “filled” state. But the issue with this is that when I change color, I now need to click on the button twice until unfilled takes effect, see the below code:

colourPalette constructor: This is where I’m calling the populateOptions() function every time I select a new color so that my button resets every time with the fill state

//Displays and handles the colour palette.
function ColourPalette() {
    //a list of web colour strings
    this.colours = ["black", "silver", "gray", "white", "maroon", "red", "purple",
        "orange", "pink", "fuchsia", "green", "lime", "olive", "yellow", "navy",
        "blue", "teal", "aqua"
    ];
    //make the start colour be black
    this.selectedColour = "black";

    var self = this;

    var colourClick = function() {
        //remove the old border
        var current = select("#" + self.selectedColour + "Swatch");
        current.style("border", "0");

        //get the new colour from the id of the clicked element
        var c = this.id().split("Swatch")[0];

        //set the selected colour and fill and stroke
        self.selectedColour = c;
        fill(c);
        stroke(c);
        circleTool.unselectTool();
        circleTool.populateOptions();
        //add a new border to the selected colour
        this.style("border", "2px solid blue");
    }

    //load in the colours
    this.loadColours = function() {
        //set the fill and stroke properties to be black at the start of the programme
        //running
        fill(this.colours[0]);
        stroke(this.colours[0]);

        //for each colour create a new div in the html for the colourSwatches
        for (var i = 0; i < this.colours.length; i++) {
            var colourID = this.colours[i] + "Swatch";

            //using JQuery add the swatch to the palette and set its background colour
            //to be the colour value.
            var colourSwatch = createDiv()
            colourSwatch.class('colourSwatches');
            colourSwatch.id(colourID);

            select(".colourPalette").child(colourSwatch);
            select("#" + colourID).style("background-color", this.colours[i]);
            colourSwatch.mouseClicked(colourClick)
        }

        select(".colourSwatches").style("border", "2px solid blue");
    };
    //call the loadColours function now it is declared
    this.loadColours();
}

circleTool constructor: This is where the populateOptions function is, where the button is controlled.

//Tool to draw circles
function CircleTool() {
    this.name = "circleTool";
    this.icon = "/assets/circle.jpg";

    var startMouseX = -1;
    var startMouseY = -1;
    var drawing = false;
    this.draw = function() {
        //Function to draw the circle
        if(mouseIsPressed) {
            if(startMouseX == -1) {
                drawing = true;
                startMouseX = mouseX;
                startMouseY = mouseY;
                loadPixels();
            }    
            else {
                updatePixels();
                ellipse(startMouseX,startMouseY,dist(startMouseX,startMouseY,mouseX,mouseY));
            }        
        }
        else if(drawing) {
            drawing = false;
            startMouseX = -1;
            startMouseY = -1;
        }
    }
    //This will clear the button from the canvas when circleTool is unselected
    this.unselectTool = function() {
        updatePixels();
        //clear options
        select(".options").html("");
    };
    //adds a button and click handler to the options area. When clicked
    //toggle the fill of the circle
    this.populateOptions = function() {
        select(".options").html(
            "<button id='circleButton'>Filled Circle</button>");
        //  //click handler
        select("#circleButton").mouseClicked(function() {
            var button = select("#" + this.elt.id);            
            if (self.axis == "fill") {
                self.axis = "notFill";
                
                button.html('Filled Circle');   
                fill(colourP.selectedColour);             
            }
            else {                
                self.axis = "fill";
                self.lineOfSymmetry = width / 2;
                noFill();
                button.html('Not Filled');
            }
            
        });
    };
}

In order to eliminate the problem of the need to click the button twice so unfilled takes effect, I tried to control all this filled/unfilled with a global variable but it did not work because even if I controled the state of the button with a boolean, the button will not update its state unless I would call the populateOptions function again.

I just want this button to control and toggle the fill state of my circle and I’m not sure how to approach this, seems an easy problem but can’t figure it out.

AI in connect 4 game using minimax algorithm not working

I’m trying to create an AI connect 4 game using the minimax algorithm for a class project. The AI was generating moves originally, but somewhere along the way when I was changing the algorithm, the AI’s moves were either not showing up or was not generated.

I thought the program might be confusing the turns, so I added the playHumanTurn function and the playAITurn function. I’ve also rearranged and adjusted various parts of the code, but I can’t seem to figure out what the issue that I’m having is. Is there anything that’s not making sense in my code or am I missing anything?

    var playerRed = "R";
var playerYellow = "Y";
var aiPlayer = playerYellow;
var currPlayer = playerRed;

var gameOver = false;
var board;

var rows = 6;
var columns = 7;
var currColumns = []; //keeps track of which row each column is at.

window.onload = function() {
    setGame();
}

function setGame() {
    board = [];
    currColumns = [5, 5, 5, 5, 5, 5, 5];

    for (let r = 0; r < rows; r++) {
        let row = [];
        for (let c = 0; c < columns; c++) {
            // JS
            row.push(' ');
            // HTML
            let tile = document.createElement("div");
            tile.id = r.toString() + "-" + c.toString();
            tile.classList.add("tile");
            tile.addEventListener("click", setPiece);
            document.getElementById("board").append(tile);
        }
        board.push(row);
    }
}

function playHumanTurn() {
    currPlayer = playerRed;
    // Add event listener for the human player's move
    document.querySelectorAll(".tile").forEach(tile => {
        tile.addEventListener("click", setPiece);
    });
}

function playAITurn() {
    currPlayer = aiPlayer;
    // Remove event listener to prevent human player from making moves during AI's turn
    document.querySelectorAll(".tile").forEach(tile => {
        tile.removeEventListener("click", setPiece);
    });

    let aiMove = getAIMove();
    let r = currColumns[aiMove];

    if (r < 0) {
        return;
    }

    board[r][aiMove] = aiPlayer;
    let tile = document.getElementById(r.toString() + "-" + aiMove.toString());
    tile.classList.add("yellow-piece");

    currColumns[aiMove] = r - 1; // Decrement after updating

    checkWinner();

    // Switch back to human player
    playHumanTurn();
}

function setPiece() {
    if (gameOver) {
        return;
    }
    
        //get coords of that tile clicked
    let coords = this.id.split("-");
    let r = parseInt(coords[0]);
    let c = parseInt(coords[1]);

    // figure out which row the current column should be on
    r = currColumns[c]; 

    if (r < 0) { // board[r][c] != ' '
        return;
    }

    board[r][c] = currPlayer; //update JS board
    let tile = document.getElementById(r.toString() + "-" + c.toString());
        tile.classList.add("red-piece");
        playAITurn();
    }

    r -= 1; //update the row height for that column
    currColumns[c] = r; //update the array

    checkWinner();


//AI move logic
function getAIMove() {
// Make a copy of the current board to avoid modifying the actual game state
const copyBoard = board.map(row => row.slice());

// Call the minimax function to find the best move for the AI
const bestMove = minimax(copyBoard, 0, aiPlayer);

return bestMove.column;
}

// Minimax algorithm
function minimax(board, depth, currentPlayer) {
// Base case: Check if the game is over or if the depth limit is reached
if (depth >= 4 || checkWinner()) {
    return { score: evaluate(board), column: -1 }; // -1 indicates no specific move column
}

const availableMoves = getAvailableMoves(board);

// If it's the AI's turn (maximizing player)
if (currentPlayer === aiPlayer) {
    let bestScore = -99999;
    let bestMove = { score: bestScore, column: -1 };

    for (const move of availableMoves) {
        const row = currColumns[move];
        if (row >= 0) {
            board[row][move] = currentPlayer;
            const score = minimax(board, depth + 1, playerRed).score;
            board[row][move] = ' '; // Undo the move

            if (score > bestScore) {
                bestScore = score;
                bestMove = { score, column: move };
            }
        }
    }

    return bestMove;
} else {
    // If it's the player's turn (minimizing player)
    let bestScore = 9999;
    let bestMove = { score: bestScore, column: -1 };

    for (const move of availableMoves) {
        const row = currColumns[move];
        if (row >= 0) {
            board[row][move] = currentPlayer;
            const score = minimax(board, depth + 1, aiPlayer).score;
            board[row][move] = ' '; // Undo the move

            if (score < bestScore) {
                bestScore = score;
                bestMove = { score, column: move };
            }
        }
    }

    return bestMove;
}
}

// Function to evaluate the current state of the board
function evaluate(board) {
    let score = 0;

    // Check for winning positions
    if (checkWinner(board, player)) {
        return player === aiPlayer ? 1000 : -1000;
    }

    // Check for threats and blocking
    score += evaluateThreats(board, player);
    score -= evaluateThreats(board, player === aiPlayer ? humanPlayer : aiPlayer);


    return score;
}

function evaluateThreats(board, player) {
    let threatScore = 0;
    // horizontal
    for (let r = 0; r < rows; r++) {
        for (let c = 0; c < columns - 2; c++){
           if (board[r][c] != ' ') {
               if (board[r][c] == board[r][c+1] && board[r][c+1] == board[r][c+2]) {
                threatScore += player === aiPlayer ? 1500 : -1500;
               }
           }
        }
   }

   // vertical
   for (let c = 0; c < columns; c++) {
       for (let r = 0; r < rows - 2; r++) {
           if (board[r][c] != ' ') {
               if (board[r][c] == board[r+1][c] && board[r+1][c] == board[r+2][c]) {
                threatScore += player === aiPlayer ? 1500 : -1500;
               }
           }
       }
   }

   // anti diagonal
   for (let r = 0; r < rows - 3; r++) {
       for (let c = 0; c < columns - 2; c++) {
           if (board[r][c] != ' ') {
               if (board[r][c] == board[r+1][c+1] && board[r+1][c+1] == board[r+2][c+2]) {
                threatScore += player === aiPlayer ? 1500 : -1500;
               }
           }
       }
   }

   // diagonal
   for (let r = 3; r < rows; r++) {
       for (let c = 0; c < columns - 2; c++) {
           if (board[r][c] != ' ') {
               if (board[r][c] == board[r-1][c+1] && board[r-1][c+1] == board[r-2][c+2]) {
                threatScore += player === aiPlayer ? 1500 : -1500;
               }
           }
       }
   }

   return threatScore;
   
}

// Function to get available moves for the AI player
function getAvailableMoves(board) {
return board[0].map((_, index) => index).filter(col => board[0][col] === ' ');
}

Using “client_secret” returned by “‘stripe checkout sessions create” API in the Elements Provider causing “Invalid value for elements()” issue

Can the client_secret returned by the stripe checkout sessions create API be used in the options of Elements Provider from “@stripe/react-stripe-js”?

If No, then what should be passed as a clientSecret in the options of Elements Provider, If PaymentElement is being embedded in it which requires the clientSecret in its wrapper Elements component?

If Yes, please suggest why I am still getting the issue even though my clientSecret format is in the correct format and directly copied from the Stripe::Checkout::Session.create API response body.

Following is the exact sample code in which I am getting errors in.

const stripePromise = loadStripe("MY Publishable key");

const ShippingAddress = () => {
  const clientSecret = 'cs_test_a1kvgZUVmZjJgvu49dSFV3HmLzH282TFslODk23jTFrn1buLPnMLstt097_secret_fidwbEhqYWAnPydgaGdgYWFgYScpJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ3dgYWx3YGZxSmtGamh1aWBxbGprJz8nZGlyZHx2J3gl';
   // clientSecret value is coming from the back-end and being set in the useEffect hook.

  return (
      <>
        {clientSecret && (
          <Elements stripe={stripePromise} options={{clientSecret}}>
           <CheckoutForm />
           {/* CheckoutForm component has PaymentElement and LinkAuthenticationElement mounted in it */}
          </Elements>)}
      </>
  );
}

export default ShippingAddress;

The browser dev console is giving me the following error but even in the error itelf, it can be seen that the value is in correct format.

React Router caught the following error during render IntegrationError: Invalid value for elements(): clientSecret should be a client secret of the form ${id}_secret_${secret}.
You specified: cs_test_a1kvgZUVmZjJgvu49dSFV3HmLzH282TFslODk23jTFrn1buLPnMLstt097_secret_fidwbEhqYWAnPydgaGdgYWFgYScpJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ3dgYWx3YGZxSmtGamh1aWBxbGprJz8nZGlyZHx2J3gl.

Please ignore the clientSecret value as it is created from test account api_keys.

How do I trigger two javaScript animations at once?

I am currently facing a problem trying to make two animations work at the same time:

  1. The first animation is to reveal hidden objects by changing the display property, and adding stylings to increase the height of a section
  2. On click, a project should scroll into view in the center

Independently, these animations work fine, however I cannot get them to work together at the same time.

I have made a CodePen to help visualise the problem and also attach the code:
Codepen

HTML

<main>
  <!--   Start of projects   -->
  <div class="wrapper">
    <!--   Project 1   -->
    <section>
      <div class="carousel">
        <ul class="ul draggable">
          <li class="container project-info"></li>
          <li class="container one hidden"></li>
          <li class="container two hidden"></li>
          <li class="container three hidden"></li>
        </ul>
      </div>
    </section>
    <!--   Project 2   -->
    <section>
      <div class="carousel">
        <ul class="ul draggable">
          <li class="container project-info"></li>
          <li class="container one hidden"></li>
          <li class="container two hidden"></li>
          <li class="container three hidden"></li>
        </ul>
      </div>
    </section>
    <!--   End of projects   -->
  </div>
</main>

CSS

:root {
  --transition: all 0.75s ease-in-out;
}
body {
  min-height: 100%;
  margin: 0;
}

/* Project list */
.wrapper {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 0rem;
  min-height: 100vh;
  justify-content: space-between;
  background: rgba(0, 0, 0, 0.5);
}
/* Default stylings for list */
.ul {
  display: flex;
  align-items: start;
  list-style-type: none;
}

/* Section for each project */
section {
  height: 30vh;
  width: 80%;
  position: relative;
  display: flex;
  justify-content: center;
  align-content: center;
  transition: var(--transition);
  transform-origin: center; /* Set the transform origin to the center */
}
/* Active state for section */
section.active {
  transform: translateX(0);
  transform-origin: center; /* Set the transform origin to the center */
}

/* Block styles */
.container {
  margin: 0 1rem;
  width: 600px;
  height: 300px;
  position: relative;
  background: red;
}
.one {
  background: blue;
}
.two {
  background: green;
}
.three {
  background: yellow;
}

/* JavaScript Classes */
/* Hidden elements */
.hidden {
  opacity: 0;
  display: none;
  transition: var(--transition); /* Transition opacity */
}
/* Show object */
.showObject {
  opacity: 1;
  animation: fadeIn 0.75s ease-in-out;
  display: flex;
  transition: var(--transition); /* Transition opacity */
}

JavaScript

gsap.registerPlugin(Draggable);
const bodyElement = document.querySelector(".wrapper");
const mainElement = document.querySelector("main");
const sections = document.querySelectorAll("section");

let isDragging = false;
let lastTouch = 0;
let isScrolling = false;
let scrollSpeed = 0;
let lastScrollTime = 0;
let lastScrollDelta = 0;
let sx = 0,
  sy = 0;
let dx = sx,
  dy = sy;
let scrollTimeout;
let expandTimeout;

sections.forEach((section) => {
  const carousel = section.querySelector(".carousel");
  let isDragging = false;
  let startX = 0;
  let endX = 0;

  // Loop through each element and make it draggable using GSAP Draggable
  document.querySelectorAll(".draggable").forEach((element) => {
    gsap.set(element, { x: 0 }); // Set initial x position

    Draggable.create(element, {
      type: "x", // Restrict movement to horizontal axis
      bounds: element.parentElement, // Restrict movement within the parent element
      edgeResistance: 1, // Simulate 'endOnly' behavior from interact.js
      onDrag: function () {
        // Update the element's transform during drag
        gsap.set(element, { x: this.x });
      },
      onRelease: function () {
        // This is similar to the 'end' listener in interact.js
        // You can add any necessary logic here when dragging ends
      }
    });
  });
  carousel.addEventListener("mousedown", (e) => {
    isDragging = true;
    startX = e.clientX || e.touches[0].clientX;
  });

  carousel.addEventListener("mouseup", (e) => {
    if (isDragging) {
      isDragging = false;
      endX = e.clientX || e.changedTouches[0].clientX;

      const distance = endX - startX;
      const momentum = 0.9; // Adjust momentum factor as needed
      const duration = 750; // Adjust duration for animation

      const distanceWithMomentum = distance * momentum;
      const targetX = carousel.scrollLeft + distanceWithMomentum;

      gsap.to(carousel, {
        scrollLeft: targetX,
        duration: duration,
        ease: "power2.out"
      });
    }
  });

  carousel.addEventListener("mouseleave", () => {
    isDragging = false;
  });
  const hidden = section.querySelectorAll(".hidden");

  if (window.innerWidth > 768) {
    section.addEventListener("click", (e) => {
      if (!isDragging && !section.classList.contains("active")) {
        sections.forEach((s) => {
          if (s !== section && s.classList.contains("active")) {
            s.classList.remove("active");
            let projectInfo = s.querySelector(".project-info");
            projectInfo
              .querySelectorAll(".hidden.showObject")
              .forEach((obj) => {
                obj.classList.remove("showObject");
              });
            s.style.transition =
              "transform 0.75s ease-in-out, height 0.75s, width 0.75s ease-in-out";
            s.style.transform = "none";
            s.style.height = "30vh";
          }
        });

        section.classList.add("active");

        setTimeout(() => {
          hidden.forEach((hide) => {
            hide.classList.add("showObject");
          });

          const expandTransitionDuration = 750;
          setTimeout(() => {
            const sectionsBefore = Array.from(sections).slice(
              0,
              Array.from(sections).indexOf(section)
            );
            const totalHeightBefore = sectionsBefore.reduce(
              (total, sec) => total + sec.clientHeight,
              0
            );
            const headerHeight = 90;
            const targetScroll =
              totalHeightBefore +
              headerHeight -
              (window.innerHeight - section.clientHeight) / 2;

            window.scrollTo({
              top: targetScroll,
              behavior: "smooth"
            });
          }, expandTransitionDuration);

          bodyElement.classList.add("expand");
          clearTimeout(expandTimeout); // Clear previous timeout if exists
          expandTimeout = setTimeout(() => {
            bodyElement.classList.remove("expand");
          }, 0); // Delay class removal after scrolling stops
        }, 750);

        section.style.transition =
          "transform 0.75s ease-in-out, height 0.75s, width 0.75s ease-in-out";
        section.style.transformOrigin = "center center";
        section.style.height = "80vh";
        section.style.width = "100%";
      }
    });
  }

  section.addEventListener("touchstart", () => {
    isDragging = true;
  });

  section.addEventListener("touchend", () => {
    isDragging = false;
    lastTouch = new Date().getTime();
  });
});

window.addEventListener("wheel", (event) => {
  const currentTime = new Date().getTime();
  const isTrackpad = currentTime - lastTouch < 100; // Adjust the time threshold as needed

  if (!isDragging) {
    const delta = event.deltaY || event.detail || -event.wheelDelta;

    if (Math.abs(delta) > 1) {
      if (
        isTrackpad ||
        event.deltaX !== undefined ||
        event.deltaY !== undefined
      ) {
        bodyElement.classList.add("expand");

        setTimeout(() => {
          bodyElement.classList.remove("expand");
        }, 100); // Adjust the delay time as needed
      }
    }
  }
});

function easeScroll() {
  sx = window.pageXOffset;
  sy = window.pageYOffset;
}

window.requestAnimationFrame(render);

function render() {
  dx = li(dx, sx, 0.07);
  dy = li(dy, sy, 0.07);
  dx = Math.floor(dx * 100) / 100;
  dy = Math.floor(dy * 100) / 100;

  mainElement.style.transform = `translate3d(-${dx}px, -${dy}px, 0px)`;

  document.body.style.height = "0";
  window.requestAnimationFrame(render);
}

function li(a, b, n) {
  return (1 - n) * a + n * b;
}

function updateScroll() {
  if (scrollSpeed !== 0) {
    window.scrollBy(0, scrollSpeed);
    scrollSpeed *= 0.9;

    if (Math.abs(scrollSpeed) < 0.1) {
      scrollSpeed = 0;
    }
  }
  requestAnimationFrame(updateScroll);
}

updateScroll();

P.S. The JavaScript is so verbose as I am using KirbyCMS to build this website, and it doesn’t support the use of modules (or at least I don’t think so), and if anybody could guide me as to how I can truncate the code that would be great. Any help would be much appreciated, thank you in advance!

So far I have tried to:

  • Tried changing the transition delay speed to match that of the animation (0.75s)

Problem with accessing a property on the prototype of document.createElement(“a”)

I’ve been trying to write a custom JavaScript console for use at school where they block inspect, and I’ve gotten quite far, but my stringify method throws an error when I try to crawl an HTMLAnchorElement. I’ve narrowed it down to an Illegal invocation for trying to access properties of the prototype of document.createElement("a").

const x = document.createElement("a");
const y = Object.getPrototypeOf(x);
console.log(y["href"]);

This code throws the Illegal invocation. However, this

const y = HTMLAnchorElement;
console.log(y["href"]);

doesn’t. I’ve looked into the Object.getOwnPropertyDescriptor(Object.getPrototypeOf(document.createElement("a")), "href").get() and that seems to be the problem. Is there a fix (perhaps binding the this value)?