How to use Puppeteer to count the occurrences of a specific text on a web page?

I am working with NodeJS and the Puppeteer library to load a website and then check if a certain text is displayed on the page. I would like to count the number of occurrences of this specific text. Specifically, I would like this search to work exactly in the same manner as how the Ctrl+F function works in Chrome or Firefox.

Here’s the code I have so far:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');

  // How do I count the occurrences of the specific text here?

  await browser.close();
})();

Can someone please help me with a solution on how to achieve this? Any help would be greatly appreciated. Thanks in advance!

Next.JS – “use client” directive produces an error

I’m having a super weird next.js issue. I just created a new project and any time I try to type "use client"; on a page, I’m met with a “SyntaxError: Unexpected token u in JSON at position 0”.

For reference, here’s my code:

"use client"

import { useRouter } from 'next/router';

export default function Page() {
  const router = useRouter();

  return (
    <div>
      <h1>Page</h1>
    </div>
  );
}

I’ve already tried to uninstall/reinstall create-next-app, but it doesn’t seem to help.

Thanks for any help!

How can I import AdminJS into my express app

I have installed the following packages @adminjs/express: ^6.0.0, @adminjs/mongoose: 4.0.0, and adminjs: ^7.0.0. In my app.js file I am importing them as per the docs as follows:

import AdminJS from 'adminjs'
import * as AdminJSExpress from '@adminjs/express'

But when I run the app I get the following error:

file:///C:/Users/USER01/github/express-template/node_modules/adminjs/lib/locale/index.js:1
import deLocale from './de/translation.json' assert { type: 'json' };
                                             ^^^^^^

SyntaxError: Unexpected identifier
    at Loader.moduleStrategy (internal/modules/esm/translators.js:145:18)

Can anyone help?
In my package.json I have set "type":"module". My node version is v14.17.3

How to import one vanilla JS file in another vanilla JS file?

I am implemanting my assignment in Vanilla JavaScript but my code is too lengthy. So, I want to devide my js file into two differnent files and import one file into another file.

Is it possible?? How it will work with html file?

I have tried type=”module” but it is not working when I open HTML file direct in browser.

How can I make this code wait in the catch block until the user enter his information

I have this React code and I am using axios to handle the request for me
If GET post throw an error this mean that the user doesn’t have an account and the system will desplay first and last name feild for the user to enter the information.

const handleSubmit = async (e) => {
        e.preventDefault();

        // post email to the database if it is not already in the database
        api.post('/login', {
            email: email,
            password: password
        })
            .then((response) => {
                navigate("/service-centers")
            })
            .catch((error) => {

                // This function will desplay the first name and last name field for the user because the user does not have an account yest
                setNameFeildVisible(true);

                // if the user enter the fisrt name and last name then post the data to the database
                api.post('/signup', {
                    email: { email },
                    password: { password },
                    first_name: { firstName },
                    last_name: { lastName }
                })
                    .then((response) => {
                        navigate("/service-centers")
                    })
            })
            .finally(function () {
                const accessToken = response?.data?.accessToken;
                setAuth({ email, password, accessToken });
                console.log("access token: ", accessToken);
            });
    }

How can I modefy the code to stop in the catch block and wait foe the user to enter the first and last name, then continue for the SignUp request located in the catch block.

Trying to repeat functions to jump between inputs

Javacript


const ignoreOtherSymbols = document.querySelector(".word .first")
let counter = 1
let word = ''
let fifthValue = ''
let row = 0
let container = document.getElementsByClassName("word")[row]
let container2 = document.getElementsByClassName("word")[row]
let inputs = document.getElementsByClassName('first')[0] // class name as number in html and increment this when user lose in press enter
function isLetter(letter) {
    return /^[a-zA-Z]$/.test(letter)
  }
  ignoreOtherSymbols.addEventListener("keydown", function (event) {
  if (!isLetter(event.key)) {
    event.preventDefault()
  }
})
container.onkeyup = function(event) {
    let target = event.srcElement || event.target
    let myLength = target.value.length
    if (isLetter(event.key) && counter < 5) {
        word += target.value
    }
    if (event.key === 'Enter' && counter == 5) {
        row++
        fifthValue = target.value
        const wordArray = word.split('')
        wordArray[4] = fifthValue
        let newWord = wordArray.join('')
        console.log(row)
        console.log(newWord)
        const apiUrl = 'https://words.dev-apis.com/word-of-the-day?puzzle=1337'
        fetch(apiUrl)
        .then(response => response.json())
        .then(data => {
        console.log(data);
        if (data.word === newWord) {
            console.log('You win')
        }
        else {
            console.log('Try again')
            console.log(row)
            if (document.getElementsByClassName("word")[row]) {
                counter = 1
                word = ''
                fifthValue = ''
                 // I need to connect this if else (mylength) to jump between inputs with the same row
                inputs.focus()
            }
        }
        })
        .catch(error => {
            console.error(error)
        })
        }
    if (event.key === 'Backspace') {
        target.value = ''
    }
    if (myLength === 1) {
        while (target = target.nextElementSibling) {
            if (target.tagName.toLowerCase() === "input") {
                if (isLetter(event.key)) {
                    target.focus()
                    counter++
                }
                break
            }
        }
    }
    console.log(counter)
    console.log(word)
}
container2.onkeydown = function(event) {
    let target = event.srcElement || event.target
    let myLength = target.value.length
    if (isLetter(event.key) && counter === 5) {
        target.value = target.value.slice(0, -1)
    }
    if (myLength === 0) {
        while (target = target.previousElementSibling) {
            if (target.tagName.toLowerCase() === "input") {
                if (event.key === 'Backspace') {
                    target.focus()
                    counter--
                    target.value = ''
                    word = word.slice(0, -1)
                }
                break
            }
     }
    }
}




HTML

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" /> <!--for emoticons, ASCII don't give emoticons-->
        <meta name="viewport" content="width=device-width, initial-scale.0" /> <!--for view side on smartphone-->
        <title>Word Master</title> <!--title of my side-->
        <link rel="stylesheet" href="index.css" />
      </head>
      <body class="page">
        <h1 class="logo">Word Masters</h1>
        <div class="line"></div>
        <div class="nothing"></div>
        <div class="letter">
            <div class="firstWord word">
                <input type="text" maxlength="1">
                <input type="text" maxlength="1">
                <input type="text" maxlength="1">
                <input type="text" maxlength="1">
                <input type="text" maxlength="1">
            </div>
            <div class="secondWord word">
                <input type="text" maxlength="1" class="first">
                <input type="text" maxlength="1" class="first">
                <input type="text" maxlength="1" class="first">
                <input type="text" maxlength="1" class="first">
                <input type="text" maxlength="1" class="first">
            </div>
            <div class="thirdWord word">
                <input type="text" maxlength="1" class="first">
                <input type="text" maxlength="1" class="first">
                <input type="text" maxlength="1">
                <input type="text" maxlength="1">
                <input type="text" maxlength="1">
            </div>
            <div class="fourthWord word">
                <input maxlength="1">
                <input maxlength="1">
                <input maxlength="1">
                <input maxlength="1">
                <input maxlength="1">
            </div>
            <div class="fifthWord word">
                <input maxlength="1">
                <input maxlength="1">
                <input maxlength="1">
                <input maxlength="1">
                <input maxlength="1">
            </div>
            <div class="sixthWord word">
                <input maxlength="1">
                <input maxlength="1">
                <input maxlength="1">
                <input maxlength="1">
                <input maxlength="1">
            </div>
        </div>
        <script src="./wordMaster.js"></script>
      </body>
</html>

I try to increment number of row if user press ‘Enter’ to start again the same what is going in container and container2. My result is that in first row everything is ok, but when focus jump to first input in second row, focus of my input don’t change when user press letter (when myLength = 1). I need some advice how to resolve this. Thank you a lot for help.

Object: null prototype {} with withCredentials axios + express

I’m trying to use my cookie created by my backend, but when I make the request the req.cookies is: [Object: null prototype] {}

How I’m creating my cookie (backend):

 const domain =
        process.env.FRONTEND_DOMAIN_TO_SHARE_COOKIES === "localhost"
            ? false
            : process.env.FRONTEND_DOMAIN_TO_SHARE_COOKIES;
    const secure =
        process.env.FRONTEND_DOMAIN_TO_SHARE_COOKIES === "localhost"
            ? false
            : true;

    res.cookie(cubikSessionCookieName, token, {
        domain,
        secure,
    });

How I’m making the request(front):

 axios.get(`my-endpoint`, {withCredentials: true})

and when I use req.cookies in my middleware I have it as null.

And if I make the request by the browser everything works as expected.. It’s something between front and back, but I don’t know where it is..

THREE.JS text inverted issue

Please understand that i cannot accurately deliver all the source code.

That issue

it printed, inverted up and down
why is it…


camera type

THREE.OrthographicCamera

part of renderer setting

constructor() {
        this.renderer = new THREE.WebGLRenderer({
            antialias: true,
            alpha: true,
            depth: false
        });

        this.renderer.setPixelRatio(window.devicePixelRatio);
        this.renderer.autoClear = false;
        this.renderer.autoClearColor = true;
        this.renderer.autoClearStencil = true;
        this.renderer.autoClearDepth = true;
}

part of camera viewport setting

__cameraViewportNear is 0.1

__cameraViewportFar is 100

    public setCameraViewport(near: number = this.__cameraViewportNear, far: number = this.__cameraViewportFar) {
        this.getCamera().left = 0;
        this.getCamera().right = this.rendererWidth;
        this.getCamera().top = 0;
        this.getCamera().bottom = this.rendererHeight;
        this.getCamera().near = near; 
        this.getCamera().far = far; 
        this.getCamera().updateProjectionMatrix();
    }

part of creating mesh

    var extrudeSettings = {
        steps: 2,
        depth: 2,
        bevelEnabled: false,
    };

    //xxx2
    let xxx_shape = self.draws().createShape();
    xxx_shape.moveTo( 34, 16.8421);
    xxx_shape.bezierCurveTo( 34, 29.4737, 17, 40, 17, 40 );
    xxx_shape.bezierCurveTo( 17, 40, 0, 29.4737, 0, 16.8421 );
    xxx_shape.bezierCurveTo( 0, 7.54047, 7.61116, 0, 17, 0 );
    xxx_shape.bezierCurveTo( 26.3888, 0, 34, 7.54047, 34, 16.8421);
    xxx_shape.closePath();

    //xxx3
    let xxx_geo = self.draws().createExtrudeGeometry(xxx_shape, extrudeSettings); // equal new three.extrudegeometry
    let xxx_material = self.draws().createMeshBaiscMeterial({color:0x4fff82}); // equal new three.meshBasicMeterial
    let xxx_mesh = self.draws().createMesh(xxx_geo, xxx_material); // equal new THREE.Mesh


    //xxx4
    const xxx_text = self.draws().createTextMesh(); // https://github.com/protectwise/troika/tree/main/packages/troika-three-text
    xxx_text.text = "HeLp1_23";
    xxx_text.fontSize = 24;
    xxx_text.position.z = 1;
    xxx_text.color = 0xff0000;
    xxx_mesh.add(xxx_text);

    //xxx5
    setTimeout(()=>{
        xxx_text.sync(()=>{
            self.events().emit("@render");
        });
    }, 0);

Error: Error creating WebGL context – Jasmine-spie

I am writing a unit test for one of the methods that instantiate a class new THREE.WebGLRenderer’ and throws an Error: Error creating WebGL context`

public init(element: HTMLElement) { 
  this.renderer = new THREE.WebGLRenderer({antialias: true}); 
}

so I decided to mock THREE.WebGLRenderer as

jasmine.createSpy('THREE.WebGLRenderer').and.returnValue(mockRenderer); 

but when I call init method it again instantiates new THREE.WebGLRenderer and throws the same error.

Someone please suggest to fix the error or help me to mock it.

how to get data from telegram bot with google app script

how to get the data from the telegram bot with google app script.

I have this script that extracts the last messages that I send to the bot from my cell phone. These messages are placed on the right of the screen.

Now I also send text to the bot from the script, and the text is placed on the left of the screen.

The question is how do I get the data on the left? Specifically, I need to get the data on the left, which is several photos.

is it possible to get them? I have tried in many ways and I have not been able to get the data that is put on the left of the screen.

    function obtenerUltimosMensajes() {
  var token = TELEGRAM_TOKEN; // Reemplazar con el token de acceso de tu bot de Telegram
  
  var apiUrl = 'https://api.telegram.org/bot' + token + '/getUpdates?limit=5';
  var response = UrlFetchApp.fetch(apiUrl);
  var jsonResponse = JSON.parse(response.getContentText());
  
  if (jsonResponse.ok) {
    var resultado = jsonResponse.result;
    if (resultado.length > 0) {
      var mensajes = [];
      for (var i = 0; i < resultado.length; i++) {
        var mensaje = resultado[i].message;
        var texto = mensaje.text;
        mensajes.push(texto);
      }
      Logger.log(mensajes);
    } else {
      Logger.log('No se encontraron mensajes en el bot');
    }
  } else {
    Logger.log('Error al obtener mensajes: ' + jsonResponse.description);
  }
}

What does this error mean? “at ” in my Javascript?

I am making a simple text color changer. There seems to be no problems in the JS. I am getting an error in the console that says “at < anonymous >”. I basically need to know what means to fix it. This is using an external JS style sheet.

Here is my code

const paragraphI = document.getElementById('paragraph');
const colors = ['black', 'blue', 'green', 'purple'];
let j = 0;

setInterval(() => {
paragraphI.style.color = colors[j];
j = (j + 1) % colors.length;
}, 1000);

NOTE I am using “j” instead of “i” because I am already using “i” in another javascript function.

I tried changing function names and making sure there wasn’t an error in my html/css. The JS isn’t running and still getting the same error. I haven’t seen it before so it threw me off.

Getting a browser notification from anybody but the current auth. user

I’m currently building a chat app in React JS and I’ve been going crazy for the past two days with this issue. I want to make it so whenever there is a new message, a browser notification appears.

The issue is that I only get a notification when I send a message, not when anybody else sends a message. It’s supposed to be literally the other way around.

I’m using the react-push-notification npm package to implement this.

Chat.js:

import { useEffect, useState } from "react";
import { addDoc, collection, serverTimestamp, onSnapshot, query, where, orderBy } from "firebase/firestore";
import { auth, db } from "../firebase-config";
import "../styles/Chat.css";
import IDLogo from "../images/IDLogo.png";
import notificationSound from "../sounds/message_sound.mp3";
import addNotification from "react-push-notification";
import DefaultProfilePicture from "../images/default_pfp.jpeg";

export const Chat = props => {
  const { room } = props;
  const [newMessage, setNewMessage] = useState();
  const [messages, setMessages] = useState([]);

  const messagesRef = collection(db, "messages");

  useEffect(() => {
    const queryMessages = query(messagesRef, where("room", "==", room), orderBy("createdAt"));
    const unsubscribe = onSnapshot(queryMessages, snapshot => {
      let messages = [];
      snapshot.forEach(doc => {
        messages.push({ ...doc.data(), id: doc.id });
      });
      setMessages(messages);
    });

    return () => unsubscribe();
  }, []);

  useEffect(() => {
    const messagesContainer = messagesRef.current;
    messagesContainer.scrollTop = messagesContainer.scrollHeight;
  }, [messagesRef]);

  const handleSubmit = async e => {
    e.preventDefault();
    if (newMessage === "") return;

    const currentUser = auth.currentUser;

    await addDoc(messagesRef, {
      text: newMessage,
      createdAt: serverTimestamp(),
      user: currentUser.displayName,
      profilePicture: currentUser.photoURL,
      room,
    });

    if (newMessage && newMessage.user !== auth.currentUser.displayName) {
      addNotification({
        title: "New Message",
        message: newMessage.text,
        duration: 5000,
        native: true,
        icon: IDLogo,
      });
    }

    setNewMessage("");
  };

  useEffect(() => {
    const audio = new Audio(notificationSound);
    const lastMessage = messages[messages.length - 1];

    if (lastMessage && lastMessage.user !== auth.currentUser.displayName) {
      audio.play();
    }
  }, [messages]);

  return (
    <div className='chat'>
      <div className='header'>
        <h1>
          Welcome to: <span className='Chat__room--title'>{room.toUpperCase()}</span>
        </h1>
      </div>

      <div ref={messagesRef} className='messages'>
        {messages.map(message => (
          <p className='Chat__message'>
            {message.profilePicture ? (
              <img className='profile__picture' referrerpolicy='no-referrer' src={message.profilePicture} alt={message.user} />
            ) : (
              <img className='profile__picture' src={DefaultProfilePicture} alt={message.user}></img>
            )}
            {message.user} : {message.text}
          </p>
        ))}
      </div>
      <div className='send__message__container'>
        <form onSubmit={handleSubmit} className='new__message__form'>
          <input
            spellCheck='false'
            onChange={e => setNewMessage(e.target.value)}
            className='new__message__input'
            placeholder='Type a message here...'
            value={newMessage}
          />
          <button type='submit' className='signOut--sendMessage__button'>
            Send
          </button>
        </form>
      </div>
    </div>
  );
};

Nuxt 3 custom string Interpolation using dynamic component

Hope you are all doing well. Just want to know if someone already tried to create their own interpolation using dynamic component. Already been fixing this for days but still can’t figure it out.

<template>
  <h4>Hi!</h4>
  <component
    :is="{
      template: dynamicTemp,
    }"
  />
</template>
<script setup lang="ts">
const firstName = "Joe";
const useInputContent = "<h3>Welcome {name}</h3>";

// Replace {name} to "Joe"
const dynamicTemp = useInputContent.replace("{name}", firstName);
</script>
// nuxt.config.ts
export default defineNuxtConfig({
  ssr: false,
});
{
  "private": true,
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },
  "devDependencies": {
    "nuxt": "^3.4.1"
  }
}

When I’m running that, I’m getting this warning: [Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue. Configure your bundler to alias “vue” to “vue/dist/vue.esm-bundler.js”.

Will really appreciate the help. Thanks

ID number in HTML higher than ID number in JS plus new node won’t add itself

I’ll be frank, not terribly skilled with JS, still learning, and I may be in way over my head but maybe not. I am creating a script that will grab a fieldset from an html form, remove it, add an ID number, iterated that ID number in the JS, then adds the now modified fieldset back. Once completed I should be able to click a button to add an additional fieldset but with now the new ID number. And repeat… I will attach a segment of my HTML and all my JS.

                <fieldset name="authorizedUsers">
                    <legend><h2>Section 2: Authorized Users</h2></legend>
                    <fieldset class="formUser" name="au">
                        <span>User ID<button>Remove</button></span>
                        <label for="auFirstName">First Name</label>
                        <input type="text" id="auFirstName" name="auFirstName" placeholder="John">
                        <label for="auLastName">Last Name</label>
                        <input type="text" id="auLastName" name="auLastName" placeholder="Smith">
                        <label for="audob">Date of Birth</label>
                        <input type="date" id="audob" name="audob">
                        <label for="auSex">Sex</label>
                        <select id="auSex" name="ausex">
                            <option value="" selected>- Select -</option>
                            <option value="male">Male</option>
                            <option value="female">Female</option>
                        </select>
                        <label for="auMaritalStatus">Marital Status</label>
                        <select id="auMaritalStatus" name="auMaritalStatus">
                            <option value="" selected>- Select -</option>
                            <option value="single">Single</option>
                            <option value="widowed">Widowed</option>
                            <option value="divorced">Divorced</option>
                            <option value="separated">Separated</option>
                            <option value="other">Other</option>
                        </select>
                        <label for="auphone">Phone Number</label>
                        <input type="tel" id="auphone" name="auphone" placeholder="123-456-7890">
                        <label for="auemail">Email</label>
                        <input type="email" id="auemail" name="aumail" placeholder="[email protected]">
                        <fieldset name="auGovID">
                            <legend>Government ID</legend>
                            <label for="auidType">ID Type</label>
                            <select id="auidType" name="auidType">
                                <option value="" selected>- Select -</option>
                                <option value="passport">Passport</option>
                                <option value="driversLicense">Drivers License</option>
                                <option value="regionalID">Regional ID</option>
                                <option value="citizenStatus">Citizen Status</option>
                            </select>
                            <label for="auidFile">Upload Government ID</label>
                            <input type="file" id="auidFile" name="auidFile">
                            <label for="auidNum">Government ID Number</label>
                            <input type="text" id="auidNum" name="auidNum" placeholder="ABC123">
                        </fieldset>
                        <fieldset name="auAddressProof">
                            <legend>Address Proof</legend>
                            <fieldset class="formFieldsetAddress" name="auAddress">
                                <legend>Residential Address</legend>
                                <label for="auAddressLine1">Address Line 1</label>
                                <span class="formInputAddress1"><input type="text" id="auAddressLine1" name="auAddressLine1"></span>
                                <label for="auAddressLine2">Address Line 2</label>
                                <span class="formInputAddress2"><input type="text" id="auAddressLine2" name="auAddressLine2"></span>
                                <label for="auCity">City/District</label>
                                <span class="formInputCity"><input type="text" id="auCity" name="auCity"></span>
                                <label for="auState">State/Province</label>
                                <span class="formInputState"><input type="text" id="auState" name="auState"></span>
                                <label for="auPostalCode">Postal Code</label>
                                <span class="formInputPostalCode"><input type="text" id="auPostalCode" name="auPostalCode"></span>
                                <label for="auCountry">Country</label>
                                <span class="formSelectCountry"><select id="auCountry" name="auCountry"></select></span>
                            </fieldset>
                            <label for="auDateAtAddress">Start Date at Residential Address</label>
                            <input type="date" id="auDateAtAddress" name="auDateAtAddress">
                            <label for="auAddressFile">Upload Proof of Address</label>
                            <input type="file" id="auAddressFile" name="auAddressFile">
                        </fieldset>
                    </fieldset>
                    <div>
                        <button type="button">Add New User</button>
                        <button>Clone from Stakeholders / Shareholders / Directors</button>
                        <button>Clone from Designated Users</button>
                    </div>
                </fieldset>
// Set the ID number to 0
let idIterator = 0;

// Grab the fieldset's parent fieldset.
const auFieldset = document.getElementsByName('authorizedUsers')[0];
const shFieldset = document.getElementsByName('stakeholders')[0];
const duFieldset = document.getElementsByName('designatedUsers')[0];

// Define the fieldset to be grabbed, clone it, then remove it
const auTemplateLocation = document.getElementsByName('au')[0];
const auTemplate = auTemplateLocation.cloneNode(true);
auTemplateLocation.remove();
const shTemplateLocation = document.getElementsByName('sh')[0];
const shTemplate = shTemplateLocation.cloneNode(true);
shTemplateLocation.remove();
const duTemplateLocation = document.getElementsByName('du')[0];
const duTemplate = duTemplateLocation.cloneNode(true);
duTemplateLocation.remove();

// Modify the "Add New User" button to add the new user
auFieldset.getElementsByTagName('div')[0].getElementsByTagName('button')[0].addEventListener('click',function(){addTemplate(auTemplate,auFieldset);},false);

// A function to target all descendants in order to add the ID's
function targetAllDescendants(node,execute) {
    node.childNodes.forEach(function(child) {
        targetAllDescendants(child,execute);
        execute(child);
    })
}

// The function that actually adds the ID
function assignID(node) {
    if(node.nodeType === 3) {
        if(node.nodeValue == 'User ID') {
            node.textContent = node.textContent+' '+idIterator;
        }
        return;
    }
    if(node.hasAttribute('id')) {
        node.setAttribute('id',idIterator+node.getAttribute('id'));
    }
    if(node.hasAttribute('for')) {
        node.setAttribute('for',idIterator+node.getAttribute('for'));
    }
    if(node.hasAttribute('name')) {
        node.setAttribute('name',idIterator+node.getAttribute('name'));
    }
}

// The function that then adds the new template with the new ID to the page and adds to the ID number
function addTemplate(template,section) {
    assignID(template);
    targetAllDescendants(template, assignID);
    template.addEventListener('click',showUser);
    const location = section.getElementsByTagName('div')[0];
    console.log(location);
    location.parentNode.insertBefore(template, location);
    idIterator ++;
    console.log(idIterator);
}

// To be done...
function showUser() {
    
}

// Attempting to add two templates...
addTemplate(auTemplate,auFieldset);
addTemplate(auTemplate,auFieldset);

What is extra odd to me is the ID in the JS increments correctly, but I get some wild numbers within the actual HTML.

With Inline script working, How can I make my external .js file work properly with my HTML file?

I’m currently working on a website utilizing ipfs & pinning services like Pinata.
My last build works flawlessly when delivered locally or through a public gateway.
When trying to pin my last build to Pinata, I was informed I can’t use inline JS.
So my next build includes an external JS file called app.js.
I’m storing this file within the same folder as my HTML file (index.html) and uploading to IPFS for testing purposes before pinning on Pinata.
I cannot for the life of me get this new build to work properly. No matter how I try to call the .js filepath, I either get 400s or 500s every single time.

    <script type="text/javascript" src="app.js"></script>

This is the line im using to call the file. I’m learning as I go so I don’t know enough about permissions or other issues that may be causing this .js file to be unretreivable.
At this point the code base is still pretty simple and I’m sure I’m missing something very obvious and just overlooking. Any help would Be appreciated!

Last build (Inline Script) example: LootCache.app

Ive tried (With the new build) every file path I can think of and nothing seems to work. I believe the JS code itself works because I simply used the last builds and externalized it. I also know atleast most of the HTML is working since the page populates. The only thing that doesn’t work is the button & the returned data when the button is pressed.

HTML :

    <!DOCTYPE html>
<html>
<head>
    <title>LootCache</title>
    <script type="text/javascript" src="app.js"></script>

    <style>
        body {
            background-color: black;
            margin: 0;
            padding: 0;
        }

        .container {
            max-width: 100%;
            margin: 0 auto;
            border: 5px solid gold;
            padding: 10px;
            box-sizing: border-box;
            display: flex;
            flex-direction: column;
            align-items: center;
            height: 100vh;
            overflow: auto;
        }

        h1 {
            font-size: 50px;
            font-weight: bold;
            color: gold;
            margin: 10px 0;
            display: flex;
            align-items: center;
        }

        .input-container {
            background-color: white;
            padding: 10px;
            margin: 10px 0;
            border-radius: 5px;
            display: flex;
            flex-direction: column;
            align-items: center;
            width: 100%;
            box-sizing: border-box;
            max-width: 800px;
        }

        input[type=text] {
            width: 100%;
            padding: 10px;
            border: none;
            border-radius: 5px;
            box-sizing: border-box;
            font-size: 16px;
            margin-bottom: 10px;
        }

        button {
            background-color: gold;
            color: black;
            border: none;
            border-radius: 5px;
            padding: 10px;
            font-size: 16px;
            cursor: pointer;
        }

            .response {
            color: gold;
            font-size: 20px;
            margin-top: 10px;
            word-wrap: break-word;
            text-align: center;
        }

        img {
            display: block;
            max-height: calc(100vh - 300px);
            margin: 10px auto;
            object-fit: contain;
            max-width: 100%;
            height: auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <img src="https://ipfs.io/ipfs/QmXjtBuhfDRZfTXZjF2dpJ4mSnmjj5RPow4qMY5X6ZQ6QU?filename=LootCache-1.png" alt="LootCache">
        <h1></h1>
        <div class="input-container">
            <label for="ethAddress">Enter Ethereum Address (No ENS):</label>
            <input type="text" id="ethAddress" name="ethAddress" placeholder="0x...">
            <button id="checkBalanceBtn">Check Your Stash!</button>
        </div>
        <div class="response" id="response"></div>
    </div>
</body>
</html>

JAVASCRIPT:

window.onload = function() {
async function getBalance(address) {
    const response = await fetch(`https://mainnet.infura.io/v3/fb3bba54ae0449c28400a4e28fb61e6c?module=account&action=balance&address=${address}`);
    const data = await response.json();
    return data.result;
}

function displayAddress() {
    var address = document.getElementById("ethAddress").value;
    var provider = new Web3.providers.HttpProvider("https://mainnet.infura.io/v3/fb3bba54ae0449c28400a4e28fb61e6c");
    var web3 = new Web3(provider);
    web3.eth.getBalance(address, function(error, balance) {
        if (error) {
            document.getElementById("response").innerHTML = "Error: " + error.message;
        } else {
            var balanceInEther = web3.utils.fromWei(balance, "ether");
            var formattedBalance = parseFloat(balanceInEther).toFixed(4);
            document.getElementById("response").innerHTML = "Address: " + address + "<br>Balance: " + formattedBalance + " ETH";
        }
    });
}
}