Search code examples
javascripthtml.netrazor

Populating large HTML div in JavaScript, based on JSON response from AJAX call


I currently have a significant amount of HTML inside a div which is generated within an iteration from a GET request to my .net core server.

Some of the values in the HTML is dynamic data returned from the server.

I've added a button to my website, which when pressed, makes an AJAX call to the server to return a JSON object representing the dynamic values for the next iteration.

So, I would like the JavaScript to add a new div to the DOM, which is the same, and has my dynamic values populated based on the JSON response.

For context, the DIV looks like this (which is from a bootstrap template I purchased):

   <div class="card">
        <!-- Card header START -->
        <div class="card-header border-0 pb-0">
            <div class="d-flex align-items-center justify-content-between">
                <div class="d-flex align-items-center">
                    <!-- Avatar -->
                    <div class="avatar me-2">
                        <a href="#!"> <img class="avatar-img rounded-circle" src="@post.ProfileImageUrl" alt="profile picture"> </a>
                    </div>
                    <!-- Info -->
                    <div>
                        <div class="nav nav-divider">
                            <h6 class="nav-item card-title mb-0"> <a href="#!"> @post.ProfileName </a></h6>
                            <span class="nav-item small"> @post.PostTime</span>
                        </div>
                    </div>
                </div>
                <!-- Card feed action dropdown START -->
                <div class="dropdown">
                    <a href="#" class="text-secondary btn btn-secondary-soft-hover py-1 px-2" id="cardFeedAction" data-bs-toggle="dropdown" aria-expanded="false">
                        <i class="bi bi-three-dots"></i>
                    </a>
                    <!-- Card feed action dropdown menu -->
                    <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="cardFeedAction">
                        <li><a class="dropdown-item" href="#"> <i class="bi bi-bookmark fa-fw pe-2"></i>Save post</a></li>
                        <li><a class="dropdown-item" href="#"> <i class="bi bi-person-x fa-fw pe-2"></i>Unfollow lori ferguson </a></li>
                        <li><a class="dropdown-item" href="#"> <i class="bi bi-x-circle fa-fw pe-2"></i>Hide post</a></li>
                        <li><a class="dropdown-item" href="#"> <i class="bi bi-slash-circle fa-fw pe-2"></i>Block</a></li>
                        <li><hr class="dropdown-divider"></li>
                        <li><a class="dropdown-item" href="#"> <i class="bi bi-flag fa-fw pe-2"></i>Report post</a></li>
                    </ul>
                </div>
                <!-- Card feed action dropdown END -->
            </div>
        </div>
        <!-- Card header END -->
        <!-- Card body START -->
        <div class="card-body">
            <p><strong>@post.PostBody</strong></p>
            <!-- Card img -->
            <img class="card-img img-fluid" src="@post.PostImageUrl" alt="Post">
            <!-- Feed react START -->
            <ul class="nav nav-stack py-3 small">
                <li class="nav-item">
                    <a class="nav-link active" href="#!"> <i class="bi bi-hand-thumbs-up-fill pe-1"></i>Liked (56)</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#!"> <i class="bi bi-chat-fill pe-1"></i>Comments (12)</a>
                </li>
                <!-- Card share action START -->
                <li class="nav-item dropdown ms-sm-auto">
                    <a class="nav-link mb-0" href="#" id="cardShareAction" data-bs-toggle="dropdown" aria-expanded="false">
                        <i class="bi bi-reply-fill flip-horizontal ps-1"></i>Share (3)
                    </a>
                    <!-- Card share action dropdown menu -->
                    <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="cardShareAction">
                        <li><a class="dropdown-item" href="#"> <i class="bi bi-envelope fa-fw pe-2"></i>Send via Direct Message</a></li>
                        <li><a class="dropdown-item" href="#"> <i class="bi bi-bookmark-check fa-fw pe-2"></i>Bookmark </a></li>
                        <li><a class="dropdown-item" href="#"> <i class="bi bi-link fa-fw pe-2"></i>Copy link to post</a></li>
                        <li><a class="dropdown-item" href="#"> <i class="bi bi-share fa-fw pe-2"></i>Share post via …</a></li>
                        <li><hr class="dropdown-divider"></li>
                        <li><a class="dropdown-item" href="#"> <i class="bi bi-pencil-square fa-fw pe-2"></i>Share to News Feed</a></li>
                    </ul>
                </li>
                <!-- Card share action END -->
            </ul>
        </div>
        <!-- Card body END -->
        <!-- Card footer START -->
        <div class="card-footer border-0 pt-0">
            <!-- Load more comments -->
            <a href="#!" role="button" class="btn btn-link btn-link-loader btn-sm text-secondary d-flex align-items-center" data-bs-toggle="button" aria-pressed="true">
                <div class="spinner-dots me-2">
                    <span class="spinner-dot"></span>
                    <span class="spinner-dot"></span>
                    <span class="spinner-dot"></span>
                </div>
                Load more comments
            </a>
        </div>
        <!-- Card footer END -->
    </div>
    <!-- Card feed item END -->

Question:

There's a lot of HTML here. What's the best way to build this out in Javascript? Is it just a case of biting the bullet and writing all of the JS code to create each element/class?

Would this be the best practice? Because it effectively means I'm maintaining the HTML markup in two places..

Thank you..!


Solution

  • I usually save the html of the template to be rendered inside an hidden element. Then I can always access it using that element.innerHTML. You could use specialized script tag instead. But the innerHTML part is the same.

    After having the HTML as string you have 2 options

    1. build the string with the values you want to populate, then add that as innerHTML to the target container.
    2. but a better approach is to create an element from that html (see function below) then append it and manipulate it otherwise.

    // render 5 cards 
    for (var i = 0; i < 5; i++) {
    
      var html = document.querySelector("#card-template").innerHTML;
      var elem = elemFromString(html)
    
      // change something
      elem.querySelector(".card-body>p strong").innerText = "card " + i
    
      // append
      document.querySelector("#container").append(elem)
    }
    
    
    // usefull utility 
    function elemFromString(html) {
      var dummy = document.createElement("div");
      dummy.innerHTML = html.trim();
      if (dummy.children.length > 1) {
        console.error("expecting one wrapping element for html. will return only firstChild")
      }
      var result = dummy.firstChild;
      result.parentNode.removeChild(result)
      return result;
    }
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
    
    <div id="container">
    </div>
    
    
    <div id="card-template" style="display:none">
      <div class="card">
        <!-- Card header START -->
        <div class="card-header border-0 pb-0">
          <div class="d-flex align-items-center justify-content-between">
            <div class="d-flex align-items-center">
              <!-- Avatar -->
              <div class="avatar me-2">
                <a href="#!"> <img class="avatar-img rounded-circle" src="@post.ProfileImageUrl" alt="profile picture"> </a>
              </div>
              <!-- Info -->
              <div>
                <div class="nav nav-divider">
                  <h6 class="nav-item card-title mb-0"> <a href="#!"> @post.ProfileName </a></h6>
                  <span class="nav-item small"> @post.PostTime</span>
                </div>
              </div>
            </div>
            <!-- Card feed action dropdown START -->
            <div class="dropdown">
              <a href="#" class="text-secondary btn btn-secondary-soft-hover py-1 px-2" id="cardFeedAction" data-bs-toggle="dropdown" aria-expanded="false">
                <i class="bi bi-three-dots"></i>
              </a>
              <!-- Card feed action dropdown menu -->
              <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="cardFeedAction">
                <li>
                  <a class="dropdown-item" href="#"> <i class="bi bi-bookmark fa-fw pe-2"></i>Save post</a>
                </li>
                <li>
                  <a class="dropdown-item" href="#"> <i class="bi bi-person-x fa-fw pe-2"></i>Unfollow lori ferguson </a>
                </li>
                <li>
                  <a class="dropdown-item" href="#"> <i class="bi bi-x-circle fa-fw pe-2"></i>Hide post</a>
                </li>
                <li>
                  <a class="dropdown-item" href="#"> <i class="bi bi-slash-circle fa-fw pe-2"></i>Block</a>
                </li>
                <li>
                  <hr class="dropdown-divider">
                </li>
                <li>
                  <a class="dropdown-item" href="#"> <i class="bi bi-flag fa-fw pe-2"></i>Report post</a>
                </li>
              </ul>
            </div>
            <!-- Card feed action dropdown END -->
          </div>
        </div>
        <!-- Card header END -->
        <!-- Card body START -->
        <div class="card-body">
          <p><strong>@post.PostBody</strong></p>
          <!-- Card img -->
          <img class="card-img img-fluid" src="@post.PostImageUrl" alt="Post">
          <!-- Feed react START -->
          <ul class="nav nav-stack py-3 small">
            <li class="nav-item">
              <a class="nav-link active" href="#!"> <i class="bi bi-hand-thumbs-up-fill pe-1"></i>Liked (56)</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#!"> <i class="bi bi-chat-fill pe-1"></i>Comments (12)</a>
            </li>
            <!-- Card share action START -->
            <li class="nav-item dropdown ms-sm-auto">
              <a class="nav-link mb-0" href="#" id="cardShareAction" data-bs-toggle="dropdown" aria-expanded="false">
                <i class="bi bi-reply-fill flip-horizontal ps-1"></i>Share (3)
              </a>
              <!-- Card share action dropdown menu -->
              <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="cardShareAction">
                <li>
                  <a class="dropdown-item" href="#"> <i class="bi bi-envelope fa-fw pe-2"></i>Send via Direct Message</a>
                </li>
                <li>
                  <a class="dropdown-item" href="#"> <i class="bi bi-bookmark-check fa-fw pe-2"></i>Bookmark </a>
                </li>
                <li>
                  <a class="dropdown-item" href="#"> <i class="bi bi-link fa-fw pe-2"></i>Copy link to post</a>
                </li>
                <li>
                  <a class="dropdown-item" href="#"> <i class="bi bi-share fa-fw pe-2"></i>Share post via …</a>
                </li>
                <li>
                  <hr class="dropdown-divider">
                </li>
                <li>
                  <a class="dropdown-item" href="#"> <i class="bi bi-pencil-square fa-fw pe-2"></i>Share to News Feed</a>
                </li>
              </ul>
            </li>
            <!-- Card share action END -->
          </ul>
        </div>
        <!-- Card body END -->
        <!-- Card footer START -->
        <div class="card-footer border-0 pt-0">
          <!-- Load more comments -->
          <a href="#!" role="button" class="btn btn-link btn-link-loader btn-sm text-secondary d-flex align-items-center" data-bs-toggle="button" aria-pressed="true">
            <div class="spinner-dots me-2">
              <span class="spinner-dot"></span>
              <span class="spinner-dot"></span>
              <span class="spinner-dot"></span>
            </div>
            Load more comments
          </a>
        </div>
        <!-- Card footer END -->
      </div>
      <!-- Card feed item END -->
    </div>