'jQuery script on dynamic ID for each instance

I have this script which enables/disables the input tag based on the previous selection. The script needs to work for every instance of a class, so the ID of the selection/input is like mieten4/preis4, mieten35/preis35, ... the problem is, even the variable names in the script need to be different, else the script won't work. So for my instance with the ID #4 the scripts needs to look like this for example:

<script>
    var $mieten4 = $('#mieten4'),
    $preis4 = $('#preis4');
$mieten4.change(function() {
    if ($mieten4.val() == 'Ja') {
        $preis4.prop( "disabled", false );
    } else {
        $preis4.prop( "disabled", true ).val('');
    }
}).trigger('change');
    </script>

          <div class="row mb-3">
          <div class="form-floating col">
          <select class="form-select" id="mieten'.$val->get_bahnid().'" name="mieten" placeholder="Mietbar?" required>
          <option value="'.$val->get_mieten().'" selected="selected">'.$val->get_mieten().'</option>
      <option '.(($val->get_mieten() === "Ja")?"style='display: none'":"").' value="Ja">Ja</option>
      <option '.(($val->get_mieten() === "Nein")?"style='display: none'":"").' value="Nein">Nein</option>
      </select>
      <label for="mieten">Mietbar?</label>
                  </div>
          <div class="form-floating col">
            <input type="text" class="form-control" id="preis'.$val->get_bahnid().'" name="preis" placeholder="Preis" pattern="[0-9,]+|auf Anfrage" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="top" title=\'Erlaubte Zeichen:<br>0-9 , "auf Anfrage"\' value="'.$val->get_preis().'" disabled required>
      <label for="preis">Preis</label>
          </div>
      </div>

To make the script work for every dynamically created ID of the object, i tried to add something like $('[id^="mieten"]').each(function (), but it didn't work for me. Also, I ask myself if it is possible to change the variable names also dynamically so the script itself will work for every instance or if there is a more simple solution to this?

I hope someone can help me with this because I don't have much experience with javascript/jQuery. Thanks in advance.



Solution 1:[1]

Here you have a demo showing how to use a template to create carbon copy elements that will be added to the container.

The template here models the sections Mieten-Preis. That's not what you strictly aimed to, but it was the quickest way for me to render on page an arbitrary amount of those components.

The very important part lays inside the change event handler that here is a generic function that will be attached as is to every mieten select and it will be able to guess the context on its own when event occurs.

Here's a reference to <template> from MDN:

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template

//Render a new pair mieten-preis inside .itemsContainer
function renderItem(){

    const template = $('#item-template');
    const newItem = template[0].content.cloneNode(true);

    const container = $('.itemsContainer');

    const itemsCount = container.find('.list-item').length;
    const newItemIndex = itemsCount+1;

    const mietenId = `mieten${newItemIndex}`;
    const preisId = `preis${newItemIndex}`;

    const newItemMietenSection = $(newItem).find('.mieten');
    const newItemPreisSection = $(newItem).find('.preis');

    newItemMietenSection.find('select')
        .prop('id', mietenId)
        .prop('name', mietenId)
        .on('change', onMietenChange);

    newItemMietenSection.find('label').prop('for', mietenId);

    newItemPreisSection.find('input')
        .prop('id', preisId)
        .prop('name', preisId);

    newItemPreisSection.find('label').prop('for', preisId);

    container.append($('<hr>'));
    container.append(newItem);
}

//on document ready it renders 10 times the "brick"
$(document).ready(()=>{
    for(let i=0;i<10;i++)
        renderItem();
});

//function to handle the change event of every mieten select
function onMietenChange(e){
    const select = e.target;
    const match = /^mieten(\d+)$/gm.exec(select.id);
    const idNumber = match[1];
    const preis = $(`#preis${idNumber}`);

    if ($(select).find('option:selected').val() == 'Ja') {
        preis.prop("disabled", false);
    } else {
        preis.prop("disabled", true).val('');
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="itemsContainer">
</div>

<template id="item-template">

  <div class="row mb-3 list-item">

    <div class="form-floating col mieten">
      <select
        class="form-select"
        placeholder="Mietbar?" required>
          <option disabled selected>Choose...</option>
          <option value="Ja">Ja</option>
          <option value="Nein">Nein</option>
      </select>
      <label>Mietbar?</label>
    </div>

    <div class="form-floating col preis">
      <label>Preis</label>
      <input
        type="text"
        class="form-control"
        placeholder="Preis"
        pattern="[0-9,]+|auf Anfrage"
        data-bs-toggle="tooltip"
        data-bs-html="true"
        data-bs-placement="top"
        title="title"
        value="" disabled required />
    </div>

  </div>
</template>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Diego De Vita