Why do I get two console logs while its should be just one ? react js

I have a simple section in react which contains two buttons logo and banner, on click I am opening a simple modal from web component, now I want when user click cancel to show me something on the console.

Problem: When I click eg Logo button which opens the modal and I click cancel I get the console log I want, and then I click the banner button and open the modal and I click cancel, instead of giving just logs related to the banner button I get both of logs.

my code Live demo : live demo

import * as element from "./custom-element";

const Images = () => {
  const [imgName, setImgName] = useState();
  const [logo, setLogo] = useState();
  const [banner, setBanner] = useState();
  const elementRef = useRef(null);

  const handleUseImage = () => {
    switch (imgName) {
      case "logo":
        console.log("logo name", imgName);
        break;
      case "banner":
        console.log("banner name", imgName);
        break;
      default:
    }
  };

  useEffect(() => {
    if (imgName) {
      elementRef.current.addEventListener("cancel", function (e) {
        handleUseImage(e);
      });
    }
  }, [imgName]);

  const handleLogo = () => {
    setImgName("logo");
    openModal();
  };
  const handleBanner = () => {
    setImgName("banner");
    openModal();
  };
  const openModal = () => {
    elementRef.current.visible = true;
  };
  return (
    <div className="App">
      <custom-element ref={elementRef}></custom-element>
      <button onClick={handleLogo}>Logo </button>
      <button onClick={handleBanner}>Banner </button>
      <span>Logo url: {logo} </span>
      <span>banner url: {banner} </span>
    </div>
  );
};

export default Images;

[enter image description here][2

Why do I get both logs?

Firebase Cloud Messaging – Error when registering service working (…ServiceWorker script evaluation failed)

I’m trying to setup up web notifications with FCM and I get error messages when trying to register the service worker.

My structure is as follows:
domain.com/notification/index.html
domain.com/notification/test-worker.js

Index.html:

function registerServiceWorker() 
{
    console.log('start register');
    return navigator.serviceWorker.register('/notification/test-worker.js')
    .then(function(registration) 
        {
            console.log('Service worker successfully registered.');
            askPermission();
            return registration;
        })
    .catch(function(err) 
        {
            console.error('Unable to register service worker.', err);
        });
    console.log('end register');
}

test-worker.js

 import { initializeApp } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-app.js";
 import { getAnalytics } from "https://www.gstatic.com/firebasejs/9.6.1/firebase-analytics.js";

 const messaging = getMessaging();
 onBackgroundMessage(messaging, (payload) => {
   console.log(' Received background message ', payload);
   // Customize notification here
  const notificationTitle = 'Background Message Title';
  const notificationOptions = {
   body: 'Background Message body.'
 };

  self.registration.showNotification(notificationTitle,
   notificationOptions);
});

The errors I get are:

Uncaught SyntaxError: Cannot use import statement outside a module
Unable to register service worker. TypeError: Failed to register a ServiceWorker for scope ('https://example.com/notification/') with script ('https://example.com/notification/test-worker.js'): ServiceWorker script evaluation failed

I understand that import cannot be used outside of a module but how can I specify that the service worker should be a module? I have seen many examples of service workers using import so I guess it should be possible to use import. Or should I use a different method?

Thanks

In the stripe customer portal, is there a way to show more information for the user’s subscription?

I have a react app that uses Stripe to process payments.

The user can create multiple messaging groups and invite other users to their groups.

The user can then “upgrade” their group by subscribing to a premium product.

I’m using the Stripe customer portal to let the users manage their subscriptions. Currently, it only shows a list of the user’s subscriptions like so:

Current Plans:

  • Premium, 9.99$ per month
  • Premium, 9.99$ per month
  • Premium, 9.99$ per month

I would like to show the name of the group associated with each subscription:

Current Plans:

  • Premium for John’s Group, 9.99$ per month
  • Premium for Greg’s Group, 9.99$ per month

How could I customize the Customer Portal?

Thank you

Use matchMedia to enable/disable script like a media query on load/viewport resize

I only want to run a script when the viewport width is greater than a set value. I would also like this to check as the browser resizes and disable/enable as required. I’ve tried to achieve this using matchMedia and rather than checking every pixel, the script only triggers when the viewport is less/greater than a set value.

If I load a narrow viewport (less than 1080px) the JS doesn’t trigger – perfect! Enlarging the viewport to have a width greater than 1080px then runs the script – also perfect!

The problem I have is when I scale down from a larger viewport (greater than 1080px) to narrow/small. The script still functions until I refresh the page – I’m hoping someone can help me with that.

As an aside, is it possible to change const mediaQuery = window.matchMedia('(min-width: 1080px)') to include a min-height or a more complex media query if required (not essential for this).

My script:

const mediaQuery = window.matchMedia('(min-width: 1080px)')

function viewportChange(e) {
  // Check if the media query is true
  if (e.matches) {
    
    $(document).ready(function() {
    
      var num_children = $('.split-loop__left').children().length;
      var child_height = $('.split-loop__right').height() / num_children;
      var half_way = num_children * child_height / 2;
      $(window).scrollTop(half_way);
    
      function crisscross() {
          
          var parent = $(".split-loop");//.first();
          var clone = $(parent).clone();
          
          var leftSide = $(clone).find('.split-loop__left');
          var rightSide = $(clone).find('.split-loop__right');
    
          if (window.scrollY > half_way ) {
              //We are scrolling up
              $(window).scrollTop(half_way - child_height);
              
              var firstLeft = $(leftSide).children().first();
              var lastRight = $(rightSide).children().last();
              
              lastRight.appendTo(leftSide);
              firstLeft.prependTo(rightSide);
    
          } else if (window.scrollY < half_way - child_height) {
    
              var lastLeft = $(leftSide).children().last();
              var firstRight = $(rightSide).children().first();
              
              $(window).scrollTop(half_way);
              lastLeft.appendTo(rightSide);
              firstRight.prependTo(leftSide);
          }
    
          $(leftSide).css('bottom', '-' + window.scrollY + 'px');
          $(rightSide).css('bottom', '-' + window.scrollY + 'px');
    
          $(parent).replaceWith(clone);        
      }
      
      $(window).scroll(crisscross);
    
    });
  }
}

// Register event listener
mediaQuery.addListener(viewportChange)

// Initial check
viewportChange(mediaQuery)

setIcon() changing Icon but not changing addressable DOM on map with large number of markers

I’m first changing the icon of a marker with .setIcon().
Then I’m rotating the icon with:

$('img[src="https://' + thedomain +
    '/xplorit_common/assets/SVG/map_radar.svg"]').css({
  'transform': 'rotate(' + adj_offset_bearing + 'deg)',
});

Everything works just fine with a small number of markers. But when I have upwards of 200+ markers the icon will not rotate. The marker image does change but the <img> src attribute does not change when viewing Elements in Dev Tools( even when searching for map_radar.svg ). Which, circling back, makes sense as to the icon not rotating; since the value is not actually addressable in the DOM. But does not make sense that the “correct” icon is visible

I’ve beaten this horse to death and need some outside input. Any ideas?

What is the meaning of a question mark in accessing an optional subobject of a Javascript object [duplicate]

This might be a really silly question and of course this has to be somewhere in the Javascript/Typescript documentation, but I couldn’t find anything about this in my googling.

Assuming I have an interface like that:

interface Test {
  a?: {
    b
  }
}

For a variable of this type I’d usually check if a is undefined before I access b like that:

if (test.a) {
  test.a.b=${test.a.b.toString();
}

However, my editor sometimes corrects my code like that if I forget to check:

test.a?.b.toString();

To me this looks like it means that b is only accessed if it is defined. However, I’d really like to understand the meaning of this better so I don’t run into troubles because of my assumptions. Could anyone point explain this to me? Or even better point me to the docs?

the server responded with a status of 500 trying to use external font in email service

I want to inject my HTML email template to email using node:

at my HTML code I import font like this:

<style type="text/css">
      
      @import url('${fontFolder}/IRANSansWeb_Light.woff');
      
      @font-face {
        font-family: "IRANSansWeb_Light";
        src: url(IRANSansWeb_Light.woff) format("truetype");
      }

...

I can download the font via the url (${fontFolder}/IRANSansWeb_Light.woff) in ethereal email which is a service for testing email but I get this error and unable to load the email:

Failed to load resource: the server responded with a status of 500 ()

Need to solve a test

Implement function verify(text) which verifies whether parentheses within text are correctly nested. You need to consider three kinds: (), [], <> and only these kinds. Examples:

verify("---(++++)----")  -> 1 
verify("") -> 1 
verify("before ( middle []) after ") -> 1  
verify(") (") -> 0 
verify("<(   >)") -> 0 
verify("(  [   <>  ()  ]  <>  )") -> 1 
verify("   (      [)") -> 0

I tried to do it as below but the interviewer told that there was an error and is giving me a second chance.

function verify(text) { 
    const stack = []; 
    for (const c of text) { 
        if (c === '(') 
            stack.unshift(')') 
        else if (c === '[') 
            stack.unshift(']') 
        else if (c === '<') 
            stack.unshift('>') 
        else if (c === stack[0]) 
            stack.shift() 
        else if (c === ')' || c === ']' || c === '>') 
            return 0 
    } 
    
}

const test_inputs = ["---(++++)----","","before ( middle []) after ", ") (", "<( >)", "( [ <> () ] <> )", " ( [)"]  
for (const i in test_inputs){
    console.log(verify(i))
}

The output is:

1
1
1
1
1
1
1

How to set the width of the antd select options to match the select box width?

I have an antd select box which has a fluid width based on its container size. I would like the options dropdown to be the same width as the select box. How can I make that happen? Currently if one of the dropdown options has a lot of text, it is expanding the width of the options dropdown. I would also like for the options content (the “footer” in the photo) to show ellipses if the text is too long (using the CSS text-overflow: ellipsis property). So I need the dropdown options to not have scrollable content, but to be cut to fit the width of the select box, then in theory the box of the option content will be restricted in width, and the CSS text overflow property will take effect.

enter image description here

How can I accomplish that here in antd?

Gmail API: Connection Refused

When calling getCode(), getToken(code), and getEmailData(token) from the below class in succession, I get the error POST http://localhost:9000/api/email net::ERR_CONNECTION_REFUSED on

const {
      data: { messagesTotal },
      errors,
    } = await this.gmailClient.users.getProfile({
      userId: "me",
    });

I’m not able to figure out why this is, and it’s the case across users, with a throttle, and with the list labels function as well. Anyone know what might be the issue?

export default class GmailClient {
  authClient;
  gmailClient;
  SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"];
  constructor() {
    //grab the credentials from the json file
    const credentials = JSON.parse(
      fs.readFileSync("controllers/credentials.json").toString()
    );
    const { client_secret, client_id, redirect_uris } = credentials.web;

    this.authClient = new google.auth.OAuth2(
      client_id,
      client_secret,
      "http://localhost:3000"
    );
    this.gmailClient = google.gmail({ version: "v1", auth: this.authClient });
  }

  async getEmailData(token) {
    // no creds saved
    if (!this.authClient.credentials?.refresh_token) {
      this.authClient.setCredentials(token);
      this.gmailClient = google.gmail({ version: "v1", auth: this.authClient });
    }

    const {
      data: { messagesTotal },
      errors,
    } = await this.gmailClient.users.getProfile({
      userId: "me",
    });

    console.log("Error", errors);

    return { labels: [`Messages total ${messagesTotal}`] };
  }

  getCode() {
    const authUrl = this.authClient.generateAuthUrl({
      access_type: "offline",
      scope: this.SCOPES,
    });
    return { authUrl };
  }

  async getToken(code) {
    try {
      const { tokens } = await this.authClient.getToken(code);
      this.authClient.setCredentials(tokens);
      return { token: tokens };
    } catch (err) {
      console.log("Error: Bad User Code: ", err);
      return res.json({ token: null });
    }
  }
}

Why I can’t add “keydown” event on td >p element?

I want to trigger some action when user press a key inside a p tag inside a td element, here is the HTML of a “td” on my table.

The problem is that when I add the keydown event to every paragraph element I don’t see any response. Here is how I did it.

document.querySelectorAll('tr').forEach((row) => {
  if (row.rowIndex > 0) {
    row.querySelectorAll('td').forEach((cell) => {
      cell.contentEditable = true;
      cell.querySelectorAll('p').forEach((prg) => {
        prg.addEventListener('keydown', () => {
          console.log('test');
        });
      });
    });
  }
});
<table>
  <tr></tr>
  <tr>
    <td data-property="phone" contenteditable="true">
      <p data-property="phone" class="sub-cell">34567890</p>
      <p data-property="phone" class="sub-cell">5511525298ss</p>
      <p data-property="phone" class="sub-cell">5511525298</p>
    </td>
  </tr>
</table>

I had some other “keydown” events on the “td” elements but when I removed them it still not working.

When I use the “click” event instead “keydown” I obtain the response that I expect.

I read here https://www.w3schools.com/Jsref/event_onkeydown.asp that keydown event is suported in “p” and table elements.

I need to obtain the p in which the text editing is happening for update server info so add the event on the table cell is not working for me.

I really apreciate any help with this, links to read or sugestions.

PD: My english is not the best so I apologize in advance.

Using jQuery contains with array

Instead of writing this a bunch of times (which works):

$('.this--class:contains("Some text")').parent().clone().appendTo($(".other--class"));

$('.this--class:contains("Other text")').parent().clone().appendTo($(".other--class"));

$('.this--class:contains("More text")').parent().clone().appendTo($(".other--class"));

If trying to figure out if I can put the text items into an array and then loop through them using jQuery and then have it clone to the .other--class.

My example:

var items = [
        "Some text",
        "Other text",
        "More text"
      ];
      $.each(items, function (index, value) {
        $(".this--class:contains(value)").parent().clone().appendTo($(".other--class"));
      }

If I do a console.log(value); I can see it kick out the correct items in the console. I’m just not sure of the correct way to have :contains() recognize that text, or if I need to go about this differently.
Overall just looking of an easier way to not have to write the same thing over and over.

HTML Form Select Element Not Allowing Changes

https://script.google.com/macros/s/AKfycbxKnhG0ErVgXSIPxczbst4o-J54dHUKC67nA4SegCnx/dev

I have a form that I am creating for my kids’ school and am having issues with the drop-down list to choose the student name. The list is populated via javascript code, importing a student list from an associated google sheet. I added a console log for each option as they are created to show that they are in fact being created. Developer options on the above link should reveal that.

Once I load the form I am unable to change the student identifier from the default option. I can’t tell what I’ve done wrong. If I hard code the values into the HTML it works fine, but I want the teacher to be able to add and remove students via the spreadsheet because that is a more user-friendly and flexible implementation.


form.html code

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

    <style>
      body {
        background: rgb(244, 235, 234)
      }

      .outer-field {
        border-radius: 15px;
        background: white;
        height: 150px;
        margin: 10px;
        padding: 20px;
      }

      .title {
        padding-left: 2%;
        font-weight: bold;
      }
    </style>
  </head>
  <body>
  
    <div class="row">
      <div class="col s8 offset-s2 offset-s2">
        <!--Document Header-->
        <div class="outer-field" style="height: 100px">
          <h4>Golden Apple Reader Book Submissions</h4>
        </div>
        <!--Form to submit ISBN and autopopulate title and page count-->
        <form id="myForm" onsubmit="event.preventDefault(); formSubmit(this) ">
          <!--Creates ISBN entry field-->
          <div class="outer-field">
            <div class="title">Book ISBN</div>
            <div class="col s8">
              <div class="input-field">
                <input type="text" id="ISBN" name="ISBN" class="UUID validate" form="myForm">
                <label for="ISBN">ISBN</label>
              </div>
            </div>
            <!--Creates button to check ISBN data-->
            <button class="btn waves-effect waves-light" id="btn" style="margin-left: 3%" type="button" onclick="populateDetails(); return false;">Autofill Book Data From ISBN
              <i class="material-icons right">send</i>
            </button>
          </div> 
          
          <!--Creates student name entry field-->
          <div class="outer-field">
            <div class="title">Student Name</div>
            <div class="input-field col s12">
              <select form="myForm" name="StudentID" id="StudentID" required>
                <!--Add student IDs and names here-->
                <!--<option value="212702">John</option>
                <option value="212703">Henry</option>
                <option value="003">003</option>-->
              </select>
            </div>
          </div>

          <!--Creates book title entry field-->
          <div class="outer-field">
            <div class="title">Book Required Information</div>
            <div class="col s8">
              <div class="input-field">
                <input type="text" id="Title" name="Title" class="name" form="myForm" required>
                <label for="Title">Book Title</label>
              </div>
            </div>
          
          <!--Creates book page count entry field-->
            <div class="col s4">
              <div class="input-field">
                <input type="number" id="PageCount" name="PageCount" class="pages" form="myForm" required>
                <label for="PageCount">Book Page Count</label>
              </div>
            </div>
          </div>
          
          <!--Creates button to submit data-->
          <button class="btn waves-effect waves-light" type="submit" name="action" style="margin-left: 3%" >Submit
            <i class="material-icons right">send</i>
          </button>
        </form>        
      </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
    <script>      
      M.FormSelect.init(document.querySelectorAll('select'));
      
      //function to populate student list element
      (function () {
        google.script.run.withSuccessHandler(
          function (selectList) {
            var select = document.getElementById('StudentID');
            for( var i=0; i<selectList.length; i++ ) {
              //initial attempt commented here for troubleshooting
              //var option = document.createElement('option');
              //option.value = selectList[i][0];
              //option.text = selectList[i][4];
              var option = new Option(selectList[i][4], selectList[i][0]);
              console.log(option);
              select.add(option, undefined);
            }
            console.log(select)
                      }
        ).getSelectList();
      }());

      //Uses the ISBN to populate the book title and page quantity
      function populateDetails(){
        isbn=document.getElementById('ISBN').value;
        //isbn=9781492680550;//for debugging only
        var url = "https://www.googleapis.com/books/v1/volumes?country=US&q=isbn:" + isbn;
        var obj
        var title="No Entry Identified";
        var pageCount=0;
        var titleout=document.getElementById('Title');
        var pageout=document.getElementById('PageCount');
        //fetches URL data from google books API
        fetch(url)
          .then(res => res.json())
          .then(data => obj = data)
          .then(
              function(settitle){
                //Assigns title to variable and text field
                title = obj.items[0].volumeInfo.title
                titleout.value=title;
                titleout.focus();
              },
              function(titlerror){
              })
          .then(
            function(setpages){
              //Assigns page count to variable and text field
              pageCount = obj.items[0].volumeInfo.pageCount
              pageout.value=pageCount;
              pageout.focus();
            },
            function(pageerror){
            })
        //In the case that no entry is found in google books API, assigns default values to text fields and deconflicts the overlapping label and value fields
        titleout.value=title;
        titleout.focus();
        pageout.value=pageCount;
        pageout.focus();
      }
      
      //Submits form data to spreadsheet
      function formSubmit (data) {        
        var dataToSubmit = {
          studentID: data.StudentID.value,
          title: data.Title.value,
          pageCount: data.PageCount.value
        }
        //Provides a success message to the user
        google.script.run.withSuccessHandler(function () {
              myForm.reset()
              M.toast({html: "Thank you! You have successfully submitted!"})
            }).submitData(dataToSubmit)
      }

    </script>
  </body>
</html>

code.gs code
”’
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(“Submissions”)
var ss2= SpreadsheetApp.getActiveSpreadsheet().getSheetByName(“Students”)
var last=ss2.getLastRow();
var students=ss2.getRange(2,1,last-1,5).getValues();
function getSelectList() {
try {
return students;
}
catch(err) {
Logger.log(err);
}
}

function doGet() {
  return HtmlService.createTemplateFromFile('form').evaluate().addMetaTag('viewport', 'width=device-width, initial-scale=1')
}

function submitData (data) {  
  ss.appendRow([new Date(),data.studentID, data.title, data.pageCount])
}
'''

spreadsheet content:
|Student |ID Number |Student Name |Teacher Name |Grade |Concatenation|
|UNK1 |John |TeacherA |K |Grade: K, Teacher: TeacherA, Name: John
|UNK2 |Henry |TeacherA |K |Grade: K, Teacher: TeacherA, Name: Henry
|UNK3 |Paige |TeacherA |K |Grade: K, Teacher: TeacherA, Name: Paige
|UNK4 |Raelyn |TeacherA |K |Grade: K, Teacher: TeacherA, Name: Raelyn
|UNK5 |Danielle |TeacherA |K |Grade: K, Teacher: TeacherA, Name: Danielle
|UNK6 |Olivia |TeacherA |K |Grade: K, Teacher: TeacherA, Name: Olivia

Array with subarray loop Javascript

Is it possible to create an array and loop throw it in JavaScript like below?

<?php
$options = array(
    'color' => array('blue', 'yellow', 'white'), 
    'size' => array('39', '40', '41'),
);

foreach($options as $option => $values){
    echo $option.'<br>';
    foreach($values as $value){
        echo $value.' ';
    }
    echo '<br>';
}
?>

I checked the internet but I can not find a good example.

Thanks for helping!

Meme Generator: Event Listener not allowing for multiple images on the page at once

const form = document.querySelector('#memeForm');
const imageInput = document.querySelector('#imageURL');
const topText = document.querySelector('#textOnTop');
const bottomText = document.querySelector('#textOnBottom');
const results = document.querySelector('#results');

form.addEventListener('submit', function(e) {
    e.preventDefault();
    const meme = document.createElement('div');
    const image = document.createElement('img');
    const textTop = document.createElement('div');
    const textBottom = document.createElement('div');
    const removeBtn = document.createElement('button');
   
    //allows file to be read by the DOM as an image?
    image.src = imageInput.value;

    
    textTop.classList.add('textTop');
    textTop.innerHTML = topText.value;

    textBottom.classList.add('textBottom');
    textBottom.innerHTML = bottomText.value;
 
    //allows the button created in line 16 and appended to the 'meme' div in line 40 to remove all contained within the parent 'meme' div
    removeBtn.innerText = "Delete Meme";
    removeBtn.addEventListener('click', function(e) {
        e.target.parentElement.remove();
    });

    
    //Does classList.add allow the append methods that follow to be added to 'meme'?
    meme.classList.add('meme');
    meme.append(image);
    meme.append(textTop);
    meme.append(textBottom);
    meme.append(removeBtn);
  
    
    //appends ALL meme inputs to the specified location(section tag)?
    results.append(meme);
    
    //clears form's inputs once form is submitted
    form.reset();

});


//cool and colorful mousemove feature!
document.addEventListener('mousemove', function(e) {
    // console.log('e.pageX, e.pageY');
    const r = Math.round(e.pageX * 255 / window.innerWidth);
    const b = Math.round(e.pageY * 255 / window.innerHeight);
    const color = `rgb(${r}, 0, ${b})`;
    document.body.style.backgroundColor = color;
    console.log(color);

});
h1 {
    font-family: 'Montserrat', sans-serif;
    display: flex;
    flex-direction: column;
    justify-content: center;
    text-align: center;
}

h4 {
    font-family: 'Montserrat', sans-serif;
    display: flex;
    flex-direction: column;
    justify-content: center;
    text-align: center;
}

label {
    font-weight: bolder;
    margin-bottom: 20px;
    font-family: 'Montserrat', sans-serif;
}

.meme {
    position: relative;
}

.textTop {
    position: absolute;
    top: 30px;
    right: 200px;
    font-family: Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif;
    z-index: 2;
    font-size: 40px;
}

.textBottom {
    position: absolute;
    font-family: Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif;
    font-size: 40px;
    z-index: 2;
    bottom: 100px;
}

.meme image {
    max-width: 100%;
    z-index: 1;
}

input {
    width: 50%;
    box-sizing: border-box;
    margin-bottom: 20px;
}

.submitBtn {
    width: 12%;
    float: right;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Meme Generator</title>
    <link rel="stylesheet" href="meme.css">
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@200&display=swap" rel="stylesheet">
</head>

<body>
    <div>
      <h1>Meme Generator</h1>
      <h4>Fill out the form to start creating memes!</h4>
    </div>
    
    <form action="" id="memeForm">
        <p>
            <label for=""> Image URL
                <input type="url" placeholder="What's the URL for your meme?" id="imageURL">
            </label>
        </p>
        <p>
            <label for=""> Text on Top
                <input type="text" placeholder="(Optional) What text do you want at the top of your meme?" id="textOnTop">
            </label>
        </p>
        <p>
            <label for=""> Text on Bottom
                <input type="text" placeholder="(Optional) What text do you want at the bottom of your meme?" id="textOnBottom">
            </label>
        </p>
        <p>
            <input type="submit" value="Create Meme!" class="submitBtn"></input>
        </p>
    </form>

    <!-- saw many people use canvas but when I tried to use that tag my image wouldn't load -->
    <div id="results"></div>

   <script src="meme.js"></script>
</body>


</html>

First post here and a relatively new coder so bear with me.

I’ve been working on this meme generator and so far it can take an image URL and text and add those to the specified div that results in something that is on the verge of looking like a meme.

I have 2 main problems:

  1. Once I fill in the inputs and press the “Create Meme!” button, the image and text are appended to their respective and elements contained within the “meme” div but I’m not able to create another meme until I delete the meme using the “Delete Meme” button I’ve created in my js file. Essentially, the “Create Meme!” button is not clickable once a meme has been generated. One of the goals of this project is for a user to add multiple memes to the page by submitting the form multiple times.

  2. I can’t figure out how to position the text that is shown on the top and bottom of the image correctly. I’m sure if I were to play with the positioning in CSS more it would look more like a standard meme but I’d rather have the text be automatically centered in the top and bottom of the image. For example, if I adjust the size of the page the image and text don’t adjust along with it.

Please let me know if I can provide more clarity on my issue, I’ve been stuck here since last night and my googling efforts are becoming more and more futile.