Search code examples
javascripthtmljquerydropdowntailwind-css

How can I generate a dropdown with Tailwind and jQuery?


So I need to make a button where it can generate a new dropdown div and can be continued until it reaches the amount that the user desires.

One of the dropdown inputs data is appended to the JSON variable I created on the script. Thing is, I have a problem with making sure that the newly generated dropdown div has the same JSON data as well. It's gone whenever I generated a new dropdown. I would love to hear some advice for me to polishing on this part, or whether this method that I used is not doable and practical enough. Thank you.

I think this is the part where I make mistakes, to append based on looping the new generated dropdown.

<div class="mx-10 my-5 border border-black p-3 formDiv1 sm:rounded-lg">
                <h1 class="level">Level 1</h1>
                <div class="grid md:grid-cols-2 py-2">
                    <label for="approval_type_1" class="col-span-1 self-center py-2">Approval Type: <span
                            class="text-red-600">*</span></label>
                    <select name="approval_type_1" id="approval_type_1" class="col-span-2 w-full rounded py-2">
                        <option selected disabled>Please select approval type...</option>
                        <option value="1">In Consultation With</option>
                        <option value="2">Endorsed by</option>
                        <option value="3">Reviewed by</option>
                        <option value="4">Approved by</option>
                    </select>
                </div>

                <div class="grid md:grid-cols-2 py-2">
                    <label for="approval_user_1" class="col-span-1 self-center py-2">Approval User: <span
                            class="text-red-600">*</span></label>
                    <select name="approval_user_1" id="approval_user_1" class="col-span-2 w-full rounded py-2 mySelect">
                        <option selected disabled>Please select a user...</option>
                    </select>
                </div>


const items = [
            {
                label: "Group Finance",
                value: "Group Finance"
            },
            {
                label: "Group Human Capital",
                value: "Group Human Capital"
            },
            {
                label: "Group Risk Management",
                value: "Group Risk Management"
            },
            {
                label: "Group Services and Communication",
                value: "Group Services and Communication",
            }
        ];

function appendOption(){
            var checkAppend = $(".mySelect").hasClass("addedList");
            $('.mySelect').addClass("addedList");
            $.each(items, function (i, item) {
                if(!checkAppend){
                    $('.mySelect').append($('<option>', { 
                        value: item.value,
                        text : item.label 
                    }));
                }
            });
        }

CodePen Example of Mine


Solution

  • jQuery is really useful, but there's a certain way it operates: when you use the ${selector}, then it finds all items in the scope (usually document) that match the selector, and provides a list of items. BUT if you start a chained operation right after the selector returns, then the operator is applied to the first element in the array. append() is such an operator.

    The other thing is that the elements you want the JSON appended to are dynamic elements: they are not there when the page loads, but are created dynamically. dynamic elements are not part of the simple $(selector) query - you have to dynamically "find" them after their creation.

    So, the solution comes in two steps:

    • you have to find the dynamically created elements
    • you have to select the last element that matched your selector.

    Here's a snippet that does both:

    const items = [{
        label: "Group 1",
        value: "Group 1"
      },
      {
        label: "Group 2",
        value: "Group 2"
      },
      {
        label: "Group 3",
        value: "Group 3"
      },
      {
        label: "Group 4",
        value: "Group 4",
      }
    ];
    
    function appendOption() {
    
      /*
        $(document).find(selector) is dynamic: it refreshes on
        every run, so it finds the dynamically created elements.
        .last() is simply a jQuery function that gets you the
        last item from the matched set.
        
        $lastItem is a jQuery() object (that's why I put a $ as
        the first char in its name), so you can call jQuery
        functions on it, like .each()
      */
      const $lastItem = $(document).find('.mySelect').last()
      var checkAppend = $lastItem.hasClass("addedList");
      lastItem.addClass("addedList");
    
      $.each(items, function(i, item) {
        if (!checkAppend) {
          $lastItem.append($('<option>', {
            value: item.value,
            text: item.label
          }));
        }
      });
    }
    
    appendOption();
    
    const getUserDataBtn = document.getElementById("get-user-data");
    const getAddApprovalBtn = document.getElementById("addApproval");
    const resetApprovalBtn = document.getElementById("resetApproval");
    
    getUserDataBtn.addEventListener(
      "click",
      () => {
        console.log($("#approval_type").val());
      },
      false
    );
    
    getAddApprovalBtn.addEventListener(
      "click",
      () => {
        const levelsLoop = $(".level").length + 1;
        const fbRenderLength = ".formDiv" + levelsLoop;
        const fbRenderLength2 = ".formDiv" + (levelsLoop - 1);
    
        if (fbRenderLength == ".formDiv2") {
          $(".formDiv1").after(function() {
            return "<div class='mx-10 my-5 border border-black p-3 sm:rounded-lg formDiv" + levelsLoop + "'>" +
              "<h1 class='level'>Level " + levelsLoop + "</h1>" +
              "<div class='grid md:grid-cols-2 py-2'>" +
              "<label for='approval_type_" + levelsLoop + "' class='col-span-1 self-center py-2'>Approval Type: <span class='text-red-600'>*</span></label>" +
              "<select name='approval_type_" + levelsLoop + "' id='approval_type_" + levelsLoop + "' class='col-span-2 w-full rounded py-2'>" +
              "<option selected disabled>Please select approval type...</option>" +
              "<option value='1'>In Consultation With</option>" +
              "<option value='2'>Endorsed by</option>" +
              "<option value='3'>Reviewed by</option>" +
              "<option value='4'>Approved by</option>" +
              "</select>" +
              "</div>" +
              "<div class='grid md:grid-cols-2 py-2'>" +
              "<label for='approval_user_" + levelsLoop + "' class='col-span-1 self-center py-2'>Approval User: <span class='text-red-600'>*</span></label>" +
              "<select name='approval_user_" + levelsLoop + "' id='approval_user_" + levelsLoop + "' class='col-span-2 w-full rounded py-2 mySelect'>" +
              "<option selected disabled>Please select a user...</option>" +
              "</select>" +
              "</div>" +
              "</div>";
          });
        } else {
          $(fbRenderLength2).after(function() {
            return "<div class='mx-10 my-5 border border-black p-3 sm:rounded-lg formDiv" + levelsLoop + "'>" +
              "<h1 class='level'>Level " + levelsLoop + "</h1>" +
              "<div class='grid md:grid-cols-2 py-2'>" +
              "<label for='approval_type_" + levelsLoop + "' class='col-span-1 self-center py-2'>Approval Type: <span class='text-red-600'>*</span></label>" +
              "<select name='approval_type_" + levelsLoop + "' id='approval_type_" + levelsLoop + "' class='col-span-2 w-full rounded py-2'>" +
              "<option selected disabled>Please select approval type...</option>" +
              "<option value='1'>In Consultation With</option>" +
              "<option value='2'>Endorsed by</option>" +
              "<option value='3'>Reviewed by</option>" +
              "<option value='4'>Approved by</option>" +
              "</select>" +
              "</div>" +
              "<div class='grid md:grid-cols-2 py-2'>" +
              "<label for='approval_user_" + levelsLoop + "' class='col-span-1 self-center py-2'>Approval User: <span class='text-red-600'>*</span></label>" +
              "<select name='approval_user_" + levelsLoop + "' id='approval_user_" + levelsLoop + "' class='col-span-2 w-full rounded py-2 mySelect'>" +
              "<option selected disabled>Please select a user...</option>" +
              "</select>" +
              "</div>" +
              "</div>";
          });
        }
        appendOption();
      },
      false
    );
    
    resetApprovalBtn.addEventListener(
      "click",
      () => {
        $(".mainDiv").html("<div class='mx-10 my-5 border border-black p-3 formDiv1 sm:rounded-lg'>" +
          "<h1 class='level'>Level 1</h1>" +
          "<div class='grid md:grid-cols-2 py-2'>" +
          "<label for='approval_type_1' class='col-span-1 self-center py-2'>Approval Type: <span class='text-red-600'>*</span></label>" +
          "<select name='approval_type_1' id='approval_type_1' class='col-span-2 w-full rounded py-2'>" +
          "<option selected disabled>Please select approval type...</option>" +
          "<option value='1'>In Consultation With</option>" +
          "<option value='2'>Endorsed by</option>" +
          "<option value='3'>Reviewed by</option>" +
          "<option value='4'>Approved by</option>" +
          "</select>" +
          "</div>" +
          "<div class='grid md:grid-cols-2 py-2'>" +
          "<label for='approval_user_1' class='col-span-1 self-center py-2'>Approval User: <span class='text-red-600'>*</span></label>" +
          "<select name='approval_user_1' id='approval_user_1' class='col-span-2 w-full rounded py-2 mySelect'>" +
          "<option selected disabled>Please select a user...</option>" +
          "</select>" +
          "</div>" +
          "</div>");
        appendOption();
      },
      false
    );
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script src="https://cdn.tailwindcss.com"></script>
    <div class="max-w-7xl mx-auto sm:px-6 lg:px-8 pb-4">
      <div class="mainDiv">
        <div class="mx-10 my-5 border border-black p-3 formDiv1 sm:rounded-lg">
          <h1 class="level">Level 1</h1>
          <div class="grid md:grid-cols-2 py-2">
            <label for="approval_type_1" class="col-span-1 self-center py-2">Approval Type: <span
                                class="text-red-600">*</span></label>
            <select name="approval_type_1" id="approval_type_1" class="col-span-2 w-full rounded py-2">
              <option selected disabled>Please select approval type...</option>
              <option value="1">In Consultation With</option>
              <option value="2">Endorsed by</option>
              <option value="3">Reviewed by</option>
              <option value="4">Approved by</option>
            </select>
          </div>
    
          <div class="grid md:grid-cols-2 py-2">
            <label for="approval_user_1" class="col-span-1 self-center py-2">Approval User: <span
                                class="text-red-600">*</span></label>
            <select name="approval_user_1" id="approval_user_1" class="col-span-2 w-full rounded py-2 mySelect fromHTML">
              <option selected disabled>Please select a user...</option>
            </select>
          </div>
          <label class="inline-flex items-center mt-3">
                    <input type="checkbox" class="form-checkbox h-5 w-5 text-gray-600" checked><span class="ml-2 text-gray-700">Add Concurrent Approver?</span>
                </label>
        </div>
      </div>
    
      <div class="mx-10 my-5">
        <button class="h-12 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded m-2" type="button" id="addApproval">Add Approval</button>
        <button class="h-12 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded m-2" type="button" id="resetApproval">Reset Approval</button>
        <button class="h-12 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded m-2" type="button" id="get-user-data">Get Updated formData</button>
      </div>
    </div>

    IMPORTANT: doing such things with every click is not efficient. You should restructure your code so that the last element is always available, you don't have to find it. But I think this is another topic.

    DISCLAIMER: I hope this answers your question, as I'm not 100% sure what you needed.