Multi-Step Form Required Fields To Go to Next Step

I’m trying to merge two codes for several days now without success. Is there someone who can help me with that please ? I’m a total beginner with web developpment, i’m sorry if it’s a mess. It’s all copy-paste essentially. The first part of the javascript is working with no issues, the second part of it has to be “added” to the first one.

/* Multistep code */

const prevBtns = document.querySelectorAll(".btn-prev");
const nextBtns = document.querySelectorAll(".btn-next");
const progress = document.getElementById("progress");
const formSteps = document.querySelectorAll(".form-step");
const progressSteps = document.querySelectorAll(".progress-step");

let formStepsNum=0;

nextBtns.forEach((btn) => {
    btn.addEventListener("click", () => {
      formStepsNum++;
      updateFormSteps();
      updateProgressbar();
    });
  });
  
  prevBtns.forEach((btn) => {
    btn.addEventListener("click", () => {
      formStepsNum--;
      updateFormSteps();
      updateProgressbar();
    });
  });
  
  function updateFormSteps() {
    formSteps.forEach((formStep) => {
      formStep.classList.contains("form-step-active") &&
        formStep.classList.remove("form-step-active");
    });
  
    formSteps[formStepsNum].classList.add("form-step-active");
  }
  
  function updateProgressbar() {
    progressSteps.forEach((progressStep, idx) => {
      if (idx < formStepsNum + 1) {
        progressStep.classList.add("progress-step-active");
      } else {
        progressStep.classList.remove("progress-step-active");
      }
    });
  
    const progressActive = document.querySelectorAll(".progress-step-active");
  
    progress.style.width =
      ((progressActive.length - 1) / (progressSteps.length - 1)) * 100 + "%";
  }

/*Validation code*/

$(document).ready(function(){
$(".btn-next").click(function(){
    var form = $("#register-user");
    form.validate({
      errorElement: 'span',
      errorClass: 'help-block',
      highlight: function(element, errorClass, validClass) {
        $(element).closest('.input-group').addClass("has-error");
      },
      unhighlight: function(element, errorClass, validClass) {
        $(element).closest('.input-group').removeClass("has-error");
      },
      rules: {
        first_name: {
          required: true,
          minlength: 3,
        },
        last_name: {
          required: true,
          minlength: 3,
        },
        user_login: {
          required: true,
        },
        user_pass: {
          required: true,
        },
        user_pass2: {
          required: true,
          equalTo: '#user_pass',
        },
        user_email: {
          required: true,
        },
        
      },      
      messages: {
        first_name: {
          required: "First name required",
        },
        last_name: {
            required: "Last name required",
        },
        user_login: {
            required: "Username required",
        },
        user_pass: {
          required: "Password required",
        },
        user_pass2: {
          required: "Password required",
          equalTo: "Password don't match",
        },
        user_email: {
          required: "Email required",
        },
      }
    });


if (form.valid() === true){
}})});
            <form name="myForm" action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="post" class="form" id="register-user">
                <!-- progress bar -->
                <div class="progressbar">
                <div class="progress" id="progress"></div>
                    <div class="progress-step progress-step-active" data-title="Intro"></div>
                    <div class="progress-step" data-title="contract"></div>
                    <div class="progress-step" data-title="ID"></div>
                    <div class="progress-step" data-title="Password"></div>
                </div>
                <!-- steps -->
                <div class="form-step form-step-active">
                        <div class="input-group">
                            <input type="text" name="first_name" id="first_name" placeholder='Prenom' required>
                        </div>
                        <div class="input-group">
                            <input type="text" name="last_name" id="last_name" placeholder='Nom' required>
                        </div>
                        <div class="input-group">
                            <input type="text" value= "<?php echo isset($_POST['user_login']) ? $_POST['user_login'] : ''?>" name="user_login" id="user_login" placeholder="Nom d'utilisateur" required>
                        </div>

                
                    <div class="">
                        <a href="#Step2" class="btn btn-next width-50 ml-auto">Suivant</a>
                    </div>
                </div>  
                <div class="form-step">
                    <div class="container">
                        <div class="input-group">
                            <input type="radio" name="occupation" id="reg_nurse" value="Registered Nurse" required>
                            <label for="reg_nurse">Registered Nurse</label>
                        </div>

                        <div class="input-group">
                            <input type="radio" name="occupation" id="prac_nurse" value="Practical Nurse" required>
                            <label for="prac_nurse">Practical Nurse</label>
                        </div>

                        <div class="input-group">
                            <input type="radio" name="occupation" id="nurse_prac" value="Nurse Practioner" required>
                            <label for="nurse_prac">Nurse Practioner</label>
                        </div>

                        <div class="input-group">
                            <input type="radio" name="occupation" id="midwife" value="Midwife" required>
                            <label for="midwife">Midwife</label>
                        </div>

                        <div class="input-group">
                            <input type="radio" name="occupation" id="reg_nurse_anes" value="Registered Nurse Anesthetist" required>
                            <label for="reg_nurse_anes">Registered Nurse Anesthetist</label>
                        </div>

                        <div class="input-group">
                            <input type="radio" name="occupation" id="nurse_spec" value="Nurse Specialist" required> 
                            <label for="nurse_spec">Nurse Specialist</label>
                        </div>

                    </div>
                        <div class="btns-group">
                            <a href="#Step1" class="btn btn-prev">Précédent</a>
                            <a href="#Step3" class="btn btn-next">Suivant</a>
                        </div>
                </div>
                        
                <div class="form-step">
                    <div class="container2">

                        <div class="input-group">
                            <input type="radio" name="contract" id="trav_cont" value="Travel Contact" required>
                            <label for="trav_cont">Travel Contact</label>
                        </div>

                        <div class="input-group">
                            <input type="radio" name="contract" id="FT_staf_pot" value="Full-time Staff Position" required>
                            <label for="FT_staf_pot">Full-time Staff Position</label>
                        </div>

                        <div class="input-group">
                            <input type="radio" name="contract" id="PT_staf_pot" value="Part-time Staff Position" required>
                            <label for="PT_staf_pot">Part-time Staff Position</label>
                        </div>

                        <div class="input-group">
                            <input type="radio" name="contract" id="not_work" value="Not Working" required>
                            <label for="not_work">Not Working</label>
                        </div>
                    </div>

                        <div class="btns-group">
                            <a href="#Step2" class="btn btn-prev">Précédent</a>
                            <a href="#Step4" class="btn btn-next">Suivant</a>
                        </div>
                </div>
                        
                <div class="form-step">
                        <div class="input-group">
                            <input type="email" value= "<?php echo isset($_POST['user_email']) ? $_POST['user_email'] : ''?>" name="user_email" id="user_email" placeholder='Votre email' required>
                        </div>

                        <div class="input-group">
                            <input type="password" name="user_pass" id="user_pass" placeholder='Votre mot de passe' required>
                        </div>

                        <div class="input-group">   
                            <input type="password" name="user_pass2" id="user_pass2" placeholder='Confirmez votre mot de passe' required>
                        </div>

                        <div class="btns-group">
                            <a href="#Step3" class="btn btn-prev">Précédent</a>
                            <input classe="btnsub" value="Inscription" type="submit"/>
                        </div>
                    </div>
            </form>

window.print() without printers causes the main process to crash in Electron App

I’m using Electron with react-js. whenever I call window.print() it shows the popup but if I tries to close it, it causes a crash and the whole window closed automatically.

btn.onClick = (e) => { 
    e.preventDefault
    window.print()
}

/* dependencies */
'version' : '17.0.1' ,
"react": "^17.0.2" ,
"electron-squirrel-startup": "^1.0.0"

So, my question is how to fix it in the Electron app, and can I use silent print in react-js if yes how I can use it?

HTML form, JS response

My is code

index.html:

<form action="https://localhost:1000/rest/api/submit-job/testhtml" method="GET">
        <div id="name-group" class="form-group">
          <label for="texta">texta</label>
          <input
            type="text"
            class="form-control"
            id="texta"
            name="texta"
          />
        </div>          
        <button type="submit" class="btn btn-success">
        
          Generate PDF
        </button>

form.js:

$(document).ready(function () {
  $("form").submit(function (event) {
    var formData = {
      texta: $("#texta").val(),
    };

    $.ajax({
      type: "GET",
      url: "https://localhost:10000/rest/api/submit-job/testhtml",
      data: formData,
      dataType: "text",
      encode: true,
    }).done(function (data) {
      console.log(data);
      document.write("<embed width='1300px' height='1250px' src=" + data + ">");      
    });
    event.preventDefault();
  });
  
});

My api returns PDF in base64 and it works just fine, but is there a better way to do it?
for example, those forms in index.html disappear and I don’t want them to disappear.
Thank you.

How to wrap the response of a json-server into a custom response

I am using the npm module json-server in order to set up a REST-API. I have set up a db.json with the following content:

{
  "home": [
        {
          "value": 1234,
          "number": 2
        },
        {
          "id": 1,
          ...
        },
        {
          "id": 2,
          ...
        }
      ]
}

I can easily return the entire array when requesting BASE_URL/home. I can also return an individual object when requesting BASE_URL/home/{id}. However, when doing so, what I would actually like to retrieve is NOT:

        {
          "id": 1,
          ...
        }

for id=1, but instead:

  [
    {
      "value": 1234,
      "number": 2
    },
    {
      "id": 1,
      ...
    }
  ]

What does this mean? – Well, I want to retrieve an array ALWAYS. I also want the first object, which contains properties “value” and “number” to ALWAYS be inside the returned array. And in addition to this I want the requested object to be inside this array.

I believe that I have to start an express server instead of the json-server in order for this to work. Unfortunately, I am not familiar with express. I have assembled an educated guess as an example, here it is:

const jsonServer = require('json-server')
const server = jsonServer.create()
const router = jsonServer.router('db.json')
const middlewares = jsonServer.defaults()

server.use(middlewares)

server.get('/api/v1/', (req, res) => {
    res.jsonp(req.query)
  })


server.use(jsonServer.bodyParser)
server.use((req, res, next) => {
  if (req.method === 'GET') {
      const alwaysReturn = {
        "value": 1234,
        "number": 2
      };
      let resArray = [];
      resArray.push(alwaysReturn);
      resArray.push(res.body)
      res.body = resArray;
  }
  next()
}) 
 
server.use(router)

This is probably very far off from a solution. Can somebody help me out?

How to find spesific Object type from string value contained in this object’s properties

I want to generate Filter pipe in Angular that works for filtering the Car, Brand and Color array object by it’s pipe parameter corresponding their “carName” , “brandName” etc. property.

For example this pipe is going to filter the Colors by their name corresponding the value of filter pipe parameter.

But I want to do that this pipe filters on all Object types with it’s(Object’s) Name property that i created wherewithal.

To do that, All Objects need to have same “Name” property name. But My Objects has different name properties like;

carName:string (Car)

name:string (Brand)

name:string (Color)

I’ve created a pipe that works successfully on object that only has “name” properties but I can’t make it successful that this pipes works on Car object as well ;

@Pipe({
 name: 'filter',
})



export class FilterPipe implements PipeTransform {

  transform(entityList: any[], filter: string): any[] {

    filteredList: typeof entityList;

if (filter) {
  filter = filter.toLocaleLowerCase();
  return entityList.filter((c) =>
    c.name.toLocaleLowerCase().includes(filter)
  );
}

return entityList;
 

 }
}

How can I create a generic Pipe to filter objects with its(Object’s) name property by just the properties of object includes “name” expression ?

Remove field depending on display type in Embed entity ckeditor Drupal

I have a specific display for document type entity. We made caption required for other media type but I want it to not be required when I select display as “document link” how can I do ?

For now I have something like this:

function MY_MODULE_form_entity_embed_dialog_alter(&$form, DrupalCoreFormFormStateInterface $form_state, $form_id){
  if (isset($form['attributes']['data-entity-embed-display'] && .....)){
    $form['attributes']['data-caption']['#required'] = FALSE;
  } else {
    $form['attributes']['data-caption']['#required'] = TRUE;
      }

The … is for adding the fact that document link option must be selected bu I don’t know how to do it.

Anyone have an idea, please ?

How to record zoom meeting and preview it on react website

I am trying to use the zoom Api to import it into my website so I can host live sessions which can also be recorded and stored for later viewing. Is there a way to do that. I am integrating zoom using the Zoom SDK which will allow me to create meetings directly from my website but I want to know if I can record and save the meeting directly on my website and save it so I can later watch it whenever I need to ?

check the alphabetical order

I am a newbie who is trying hard to have a grip on javascript. please help me to consolidate my fundamentals.

input will be a string of letters.

following are the requirements.

function should return true if following conditions satisfy:

  1. letters are in alphabetical order. (case insensitive)

  2. only one letter is passed as input. example :

isAlphabet (‘abc’) === true

isAlphabet (‘aBc’) === true

isAlphabet (‘a’) === true

isAlphabet (‘mnoprqst’) === false

isAlphabet (”) === false

isAlphabet (‘tt’) === false

function isAlphabet(letters) {
    
    const string = letters.toLowerCase();
    
    for (let i = 0; i < string.length; i++) {
        
        const diff = string.charCodeAt(i + 1) - string.charCodeAt(i);
        
        if (diff === 1) {
            
            continue;
            
        } else if (string === '') {
            
            return false;
            
        } else if (string.length === 1) {
            
            return true;
            
        } else {
            
            return false;
            
        }
        
    }
    
    return true;
    
}

How to prevent function inside another function to refresh my form details?

I have a function that is supposed to submit card details to Stripe.
The stripe.confirmCardPayment is inside handleSubmit and while debugging I realized that my form details clears whenever a function is called ( tokenize() or confirmCardPayment() )
How can I prevent that ?

The handleSubmit function:

//Submit payment to Stripe and handle error
    const handleSubmit = async(e) => {
        
        e.preventDefault();
        console.log('CheckoutHandleSubmit');

        //verify instance and token is there
        if(!stripe || !elements || !token || !data){
            console.log('return !')
            return
        }

        //Get CardElement and attach to DOM
        const cardElement = elements.getElement(CardElement);
        cardElement.mount('#card-element');
        debugger

        //console.log('creating payment intent');
        //console.log('myData:', data);
        //console.log('beforeTokenizing:');

        //Tokenize card details and handle errors
            
            try{
                
                console.log('HEYY:', stripe)
                const payload = await stripe.confirmCardPayment(
                    data.info.clientSecret, 
                    {
                        payment_method: {
                          card: cardElement,
                          //billing_details: { name: formData.lastnameInput },
                        },
                    }
                )
                //console.log('payload:', payload)

            }catch(err){
                console.log('err:', err)
            }
            
        const tokenize = await swell.payment.tokenize({
            card: {
                onError: (err) => {
                    //inform the customer there was an error
                    if(err){
                        console.log('error:', err);
                    }   
                    //setProcessing(false);
                },
                onSuccess: async() => {

                    //submit the form
                }   
            }
        })
    }; 

Dynamically set div position after closing it

I’m trying to save div position in place that I’m closing it, so after I reopen that div it will appear in place it was when I clicked CLOSE button. I’ve tried set style with js setAttribute() in dragMoveListener function, using props in styled components and passing them to Window styled.div with useState hook and none of it worked Here’s the code:

Dragging.js:

import interact from "https://cdn.interactjs.io/v1.10.11/interactjs/index.js";
import styled from "styled-components";

const Window = styled.div`
  position: fixed;
  width: 500px;
  height: 300px;
  border-radius: 8px;
  border: solid black;
  padding: 20px;
  background-color: white;
  color: black;
  z-index: 10;
  touch-action: none;
  box-sizing: border-box;
  left: 0;
  right: 0;
  margin-left: auto;
  margin-right: auto;
`;

const Dragging = ({ children }) => {
  interact(".resize-drag")
    .resizable({
      edges: { left: true, right: true, bottom: true, top: true },

      listeners: {
        move(event) {
          var target = event.target;
          var x = parseFloat(target.getAttribute("data-x")) || 0;
          var y = parseFloat(target.getAttribute("data-y")) || 0;

          target.style.width = event.rect.width + "px";
          target.style.height = event.rect.height + "px";

          x += event.deltaRect.left;
          y += event.deltaRect.top;

          target.style.transform = "translate(" + x + "px," + y + "px)";

          target.setAttribute("data-x", x);
          target.setAttribute("data-y", y);
        },
      },
      modifiers: [
        interact.modifiers.restrictSize({
          min: { width: 200, height: 200 },
        }),
      ],

      inertia: true,
    })
    .draggable({
      inertia: true,
      autoScroll: true,
      listeners: {
        move: dragMoveListener,
      },
    });

  function dragMoveListener(event) {
    var target = event.target;
    var x = (parseFloat(target.getAttribute("data-x")) || 0) + event.dx;
    var y = (parseFloat(target.getAttribute("data-y")) || 0) + event.dy;

    target.style.transform = "translate(" + x + "px, " + y + "px)";

    target.setAttribute("data-x", x);
    target.setAttribute("data-y", y);
  }

  return <Window className="resize-drag">{children}</Window>;
};

export default Dragging;

DeviceModal.js:

import React, { useEffect, useState } from "react";
import { Loader, Button, Header } from "semantic-ui-react";
import styled from "styled-components";
import DeviceInfo from "./DeviceInfo";
import Dragging from "./Dragging";

const WindowBody = styled.div`
  padding: 10px;
  border-top: 1px solid #eee;
  border-bottom: 1px solid #eee;
`;

const DeviceModal = (props) => {
  const [data, setData] = useState();
  const id = props.id - 1;

  useEffect(() => {
    const fetchDevice = async () => {
      try {
        const response = await fetch(
          `http://localhost:5000/api/v1/devices/${id}`
        );
        const responseData = await response.json();
        setData(responseData);
      } catch (err) {
        if (err.value) {
          console.log(err);
        }
      }
    };

    fetchDevice();
  }, [id]);

  if (!props.show) {
    return null;
  }

  if (data) {
    return (
      <Dragging>
        {
          <>
            <Header>Info:</Header>
            <WindowBody>
              <DeviceInfo data={data} />
            </WindowBody>
            <Button style={{ padding: "2px" }} onClick={props.onClose}>
              CLOSE
            </Button>
          </>
        }
      </Dragging>
    );
  } else {
    return <Dragging>{<Loader />}</Dragging>;
  }
};

export default DeviceModal;

I tried lot of things but none of those worked so please help.

How to use only hover or only focus in SCSS?

I have my Vue input component. This component has such styles:

.basic-input:hover { box-shadow: 0 0 5px $maincolor; }
.basic-input:focus { border: 0.5px solid $maincolor; }

As you can see it has box shadow on hover and border on focus. Here is the problem, if I have focus and hover at the same time, I has box shadow and border.

I want to make it work like this – if it’s hover or focus, it has to be like it is, but If it has focus and hover at the same time, I want to use only border. I was trying to do this like that, but it doesn’t work:

.basic-input:focus .basic-input:hover { border: 0.5px solid $maincolor; }

Error: Cannot find module ‘core-js/modules/es.object.define-property.js’

error – Error: Cannot find module ‘core-js/modules/es.object.define-property.js’
Require stack:

  • E:JibonvaiPixer-Laraveladminnode_modulesnext-i18nextdistcommonjsindex.js
  • E:JibonvaiPixer-Laraveladmin.nextserverpages_document.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistserverrequire.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistserverload-components.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistbuildutils.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistbuildoutputstore.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistbuildoutputindex.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistclinext-dev.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistbinnext
    Error: Cannot find module ‘core-js/modules/es.object.define-property.js’
    Require stack:
  • E:JibonvaiPixer-Laraveladminnode_modulesnext-i18nextdistcommonjsindex.js
  • E:JibonvaiPixer-Laraveladmin.nextserverpages_document.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistserverrequire.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistserverload-components.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistbuildutils.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistbuildoutputstore.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistbuildoutputindex.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistclinext-dev.js
  • E:JibonvaiPixer-Laraveladminnode_modulesnextdistbinnext
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:933:15)
    at Function.mod._resolveFilename (E:JibonvaiPixer-Laraveladminnode_modulesnextdistbuildwebpackrequire-hook.js:171:28)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object. (E:JibonvaiPixer-Laraveladminnode_modulesnext-i18nextdistcommonjsindex.js:3:1)
    at Module._compile (node:internal/modules/cjs/loader:1103:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1155:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12) {
    code: ‘MODULE_NOT_FOUND’,
    requireStack: [
    ‘E:JibonvaiPixer-Laraveladminnode_modulesnext-i18nextdistcommonjsindex.js’,
    ‘E:JibonvaiPixer-Laraveladmin.nextserverpages_document.js’,
    ‘E:JibonvaiPixer-Laraveladminnode_modulesnextdistserverrequire.js’,
    ‘E:JibonvaiPixer-Laraveladminnode_modulesnextdistserverload-components.js’,
    ‘E:JibonvaiPixer-Laraveladminnode_modulesnextdistbuildutils.js’,
    ‘E:JibonvaiPixer-Laraveladminnode_modulesnextdistbuildoutputstore.js’,
    ‘E:JibonvaiPixer-Laraveladminnode_modulesnextdistbuildoutputindex.js’,
    ‘E:JibonvaiPixer-Laraveladminnode_modulesnextdistclinext-dev.js’,
    ‘E:JibonvaiPixer-Laraveladminnode_modulesnextdistbinnext’
    ]
    }

package.json

{
“name”: “@pixer/admin-rest”,
“version”: “1.0.0”,
“private”: true,
“scripts”: {
“dev”: “next dev -p 3002”,
“build”: “next build”,
“start”: “next start -p 3002”
},
“dependencies”: {
“@fontsource/open-sans”: “4.5.2”,
“@headlessui/react”: “1.4.2”,
“@hookform/resolvers”: “2.8.4”,
“@reach/portal”: “0.16.2”,
“@react-google-maps/api”: “2.7.0”,
“@react-pdf/renderer”: “2.0.21”,
“apexcharts”: “3.31.0”,
“axios”: “0.24.0”,
“body-scroll-lock”: “4.0.0-beta.0”,
“camelcase-keys”: “7.0.1”,
“classnames”: “2.3.1”,
“cookie”: “0.4.1”,
“dayjs”: “1.10.7”,
“framer-motion”: “5.3.3”,
“jotai”: “1.4.6”,
“js-cookie”: “3.0.1”,
“lodash”: “4.17.21”,
“next”: “12.0.7”,
“next-pwa”: “5.4.4”,
“next-seo”: “4.28.1”,
“overlayscrollbars”: “1.13.1”,
“overlayscrollbars-react”: “0.2.3”,
“rc-pagination”: “3.1.14”,
“rc-progress”: “3.1.4”,
“rc-table”: “7.21.0”,
“react”: “17.0.2”,
“react-apexcharts”: “1.3.9”,
“react-datepicker”: “4.5.0”,
“react-dom”: “17.0.2”,
“react-dropzone”: “11.4.2”,
“react-hook-form”: “7.20.5”,
“react-laag”: “2.0.3”,
“react-phone-input-2”: “2.14.0”,
“react-query”: “3.34.0”,
“react-scroll”: “1.8.4”,
“react-select”: “5.2.1”,
“react-toastify”: “8.1.0”,
“react-use”: “17.3.1”,
“swiper”: “7.3.1”,
“yup”: “0.32.11”
},
“devDependencies”: {
“@types/body-scroll-lock”: “3.1.0”,
“@types/js-cookie”: “3.0.1”,
“@types/lodash”: “4.14.177”,
“@types/node”: “16.11.11”,
“@types/overlayscrollbars”: “1.12.1”,
“@types/react”: “17.0.37”,
“@types/react-datepicker”: “4.3.2”,
“autoprefixer”: “10.4.0”,
“next-i18next”: “10.0.1”,
“postcss”: “8.4.4”,
“tailwindcss”: “2.2.19”,
“tailwindcss-rtl”: “0.8.0”,
“typescript”: “4.5.2”
}
}

How can I run a JavaScript function when the URI source of an image renders?

Background

My big-picture goal is to display an image such that the following conditions are satisfied:

  • The entire viewport is filled by the image
  • At least one dimension of the image fits within the viewport
  • The image is not stretched

My solution is contained in the function updateRatioSettings, which works as follows:

  1. Determine the aspect ratios of the source image (using naturalWidth and naturalHeight) and the viewport
  2. If the viewport is wider than the image, width = "100%" and height = "auto".
  3. If the image is wider than the viewport, height = "100%" and width = "auto".

This works!

The Problem

Now I just need to get updateRatioSettings to run when either of the aspect ratios changes. I have a working connection to the resize event of window, so now I just need to figure out how to make the function run when I change the image source.

Each of the below code snippets shows a square centered in a horizontal viewport. A working solution will show the entire viewport filled with this nice grey color.

Attempt #1

My first strategy was to call updateRatioSettings immediately after setting img.src. However, I ran into an issue where sourceRatio, which is calculated as img.naturalWidth / img.naturalHeight, printed as NaN.

// function to listen for window size changes and move image accordingly
let img;
let windowWider = false; // assume the screen is some ratio narrower than the image... to start at least
let ratioUpdateReady = true; // debounce variable

function updateRatioSettings() {
    // if the function is running, don't run the function
    if (ratioUpdateReady) {
        ratioUpdateReady = false;
        // begin debounced code
        
        const sourceRatio = img.naturalWidth / img.naturalHeight;
        const windowRatio = window.innerWidth / window.innerHeight;
        console.log("sourceRatio: " + sourceRatio, "windowRatio: " + windowRatio);
        if (windowRatio > sourceRatio != windowWider) { // if something has changed
            windowWider = !windowWider // log the new status
            if (windowWider) {
                // settings for window aspect ratio > source aspect ratio
                img.style.width = "100%";
                img.style.height = "auto";
            } else {
                // settings for source aspect ratio > window aspect ratio
                img.style.height = "100%";
                img.style.width = "auto";
            }
        }
        
        // end debounced code
        ratioUpdateReady = true;
    }
}

document.addEventListener('DOMContentLoaded', function(){
    // identify the element containing the image
    img = document.getElementById("background");
    
    // load an initial image
  (function (){ // this normally executes after sending a request to another part of the chrome extension
    const uri = "data:image/bmp;base64,Qk06AAAAAAAAADYAAAAoAAAAAQAAAAEAAAABABgAAAAAAAAAAADDDgAAww4AAAAAAAAAAAAAQEBAAA=="; // this is the string returned by the extension in my test case
        img.src = uri;
        updateRatioSettings(); // this gets a sourceRatio that is NaN
  })()
    
    // set up an event to update the ratio settings when the window resizes
    // commenting out for the purpose of this question, since it doesn't need debugging
    // window.addEventListener('resize', updateRatioSettings);
});
html,body{
    margin:0;
    height:100%;
    overflow:hidden;
}
img{
    height:100%;
    width:auto;
    position:absolute;
    top:-100%; bottom:-100%;
    left:-100%; right:-100%;
    margin:auto;
}
<html>
    <head>
        <title>New tab</title>
    </head>
    <body>
        <img id="background" src="" alt="">
    </body>
</html>

Attempt #2

Here I attempted to use image.onLoad to run updateRatioSettings when the image URI is loaded.

// function to listen for window size changes and move image accordingly
let img;
let windowWider = false; // assume the screen is some ratio narrower than the image... to start at least
let ratioUpdateReady = true; // debounce variable

function updateRatioSettings() {
    // if the function is running, don't run the function
    if (ratioUpdateReady) {
        ratioUpdateReady = false;
        // begin debounced code
        
        const sourceRatio = img.naturalWidth / img.naturalHeight;
        const windowRatio = window.innerWidth / window.innerHeight;
        console.log("sourceRatio: " + sourceRatio, "windowRatio: " + windowRatio);
        if (windowRatio > sourceRatio != windowWider) { // if something has changed
            windowWider = !windowWider // log the new status
            if (windowWider) {
                // settings for window aspect ratio > source aspect ratio
                img.style.width = "100%";
                img.style.height = "auto";
            } else {
                // settings for source aspect ratio > window aspect ratio
                img.style.height = "100%";
                img.style.width = "auto";
            }
        }
        
        // end debounced code
        ratioUpdateReady = true;
    }
}

document.addEventListener('DOMContentLoaded', function(){
    // identify the element containing the image
    img = document.getElementById("background");
    
    // load an initial image
  (function (){ // this normally executes after sending a request to another part of the chrome extension
    const uri = "data:image/bmp;base64,Qk06AAAAAAAAADYAAAAoAAAAAQAAAAEAAAABABgAAAAAAAAAAADDDgAAww4AAAAAAAAAAAAAQEBAAA=="; // this is the string returned by the extension in my test case
    img.onLoad = updateRatioSettings; // this is the function I want to run when naturalWidth and naturalHeight have updated
        img.src = uri; // updateRatioSettings is not called
  })()
    
    // set up an event to update the ratio settings when the window resizes
    // commenting out for the purpose of this question, since it doesn't need debugging
    // window.addEventListener('resize', updateRatioSettings);
});
html,body{
    margin:0;
    height:100%;
    overflow:hidden;
}
img{
    height:100%;
    width:auto;
    position:absolute;
    top:-100%; bottom:-100%;
    left:-100%; right:-100%;
    margin:auto;
}
<html>
    <head>
        <title>New tab</title>
    </head>
    <body>
        <img id="background" src="" alt="">
    </body>
</html>

Attempt #3

Next, I tried using a MutationObserver to watch for the point at which naturalWidth and naturalHeight actually update, then run updateRatioSettings in repsonse. Like in Attempt #2, updateRatioSettings never actually ran.

// function to listen for window size changes and move image accordingly
let img;
let windowWider = false; // assume the screen is some ratio narrower than the image... to start at least
let ratioUpdateReady = true; // debounce variable

function updateRatioSettings() {
    // if the function is running, don't run the function
    if (ratioUpdateReady) {
        ratioUpdateReady = false;
        // begin debounced code
        
        const sourceRatio = img.naturalWidth / img.naturalHeight;
        const windowRatio = window.innerWidth / window.innerHeight;
        console.log("sourceRatio: " + sourceRatio, "windowRatio: " + windowRatio);
        if (windowRatio > sourceRatio != windowWider) { // if something has changed
            windowWider = !windowWider // log the new status
            if (windowWider) {
                // settings for window aspect ratio > source aspect ratio
                img.style.width = "100%";
                img.style.height = "auto";
            } else {
                // settings for source aspect ratio > window aspect ratio
                img.style.height = "100%";
                img.style.width = "auto";
            }
        }
        
        // end debounced code
        ratioUpdateReady = true;
    }
}

const watchingAttributes = ["naturalWidth", "naturalHeight"];

document.addEventListener('DOMContentLoaded', function(){
  // identify the element containing the image
  img = document.getElementById("background");

  // set up the image resize listener
  function processMutation(mutationRecord) {
    if (watchingAttributes.includes(mutationRecord.attributeName)) {
      updateRatioSettings();
    }
  }
  let watcher = new MutationObserver((mutations) => {
    mutations.forEach(processMutation);
  });
  watcher.observe(img, {
    attributes: true
  });

  // load an initial image
  (function (){ // this normally executes after sending a request to another part of the chrome extension
    const uri = "data:image/bmp;base64,Qk06AAAAAAAAADYAAAAoAAAAAQAAAAEAAAABABgAAAAAAAAAAADDDgAAww4AAAAAAAAAAAAAQEBAAA=="; // this is the string returned by the extension in my test case
    img.src = uri;
  })()
    
  // set up an event to update the ratio settings when the window resizes
  // commenting out for the purpose of this question, since it doesn't need debugging
  // window.addEventListener('resize', updateRatioSettings);
});
html,body{
    margin:0;
    height:100%;
    overflow:hidden;
}
img{
    height:100%;
    width:auto;
    position:absolute;
    top:-100%; bottom:-100%;
    left:-100%; right:-100%;
    margin:auto;
}
<html>
    <head>
        <title>New tab</title>
    </head>
    <body>
        <img id="background" src="" alt="">
    </body>
</html>

Conclusion

So… what do I do now?

Add php into js urlencoded.append [duplicate]

I’m pulling customer session data like so:

get('CustomerModelSession');
    if($customer_session->isLoggedIn()) 
    {
        
echo   "

".$customer_session->getCustomer()->getId();


echo   "

".$customer_session->getCustomer()->getName()";


echo   "

".$customer_session->getCustomer()->getEmail()";


echo   "

".$customer_session->getCustomer()->getGroupId()";

How do I add php variables from the session data above into the urlencoded.apend?

var urlencoded = new URLSearchParams();
urlencoded.append("email", "[email protected]");
urlencoded.append("tag_string", "VP");

urlencoded.append("first_name", "nate");
urlencoded.append("last_name", "davis");
urlencoded.append("addresses[0][address1]", "124 Main");

I’m assuming it’s just like this?

urlencoded.append("first_name", "<?php echo ".$customer_session->getCustomer()->getName()"; ?>");

Any help would be greatly appreciated.