0

I have an html element tree sample(below) that I want to return with relevant data for every match I got in database.Lets say there are 5 matches. Do I need to create 5 given elements and populate them with javascript data?

I'm gonna run a loop, but this looks like it will be performance costly(creating all element tree for every match). Instead, can i use the given element(pic) populate it with javascript and drop it onto dom instead (x times)? If possible how ?

<!-- sample elem -->
<div class="col-12 col-md-4" style="display: none">
    <div class="card my-3 mx-1">
        <a href="#"><img src="" alt="img"></a>
        <div class="card-body">
            <div class="row">
                <div class="col-12 p-1">Country</div>
                <div class="col-3 p-1">State</div>
                <div class="col-4 p-1">City</div>
            </div>
        </div>
    </div>
</div>
4
  • 1
    Please don't post pictures of code, post the actual code as text. Commented Mar 15, 2020 at 20:38
  • I tried posting code but somehow it didn't formatted well visually. Commented Mar 15, 2020 at 20:47
  • 3
    The expensive part is not actually creating this nodes in memory and injecting them: but actually injecting nodes iteratively, multiple times, which forces the document to reflow. More often than not that is the most expensive part of JS operation. What you could do is to use a generic method to create these elements, and then insert it into a document fragment. Once that is done, you insert the document fragment in its entirety into the dom ONCE. Commented Mar 15, 2020 at 20:48
  • @cassidy Use the code formatting function in the editor when you paste code. It is indicated by the {} button. Commented Mar 15, 2020 at 20:48

2 Answers 2

1

To further elabourate on my comment: it is often the repeated insertion of elements into the DOM tree that causes performance issue, because the document needs to reflow every time a new node is inserted. You should not be worried about calling/invoking document.createElement() too many times: that is the least of your concern.

Therefore, I would suggest that you use a function to create your entire sample element. You can then invoke this function to create the entire card element as you please in each iteration of the loop, and then append it to the document fragment.

Pseudo code:

function createCard() {
  // Create the entire `sample element` as you would call it
  const el = <something>;
  return el;
}

// Create new document fragment to hold all the nodes
// At this point, we are NOT injecting them into the DOM yet
const fragment = new DocumentFragment();

// Go through your data and create new card for each data point
for (let i = 0; i < 5; i++) {
    fragment.appendChild(createCard());
}

// Now this is when you insert the entire bulk of the content into the DOM
document.querySelector('#myInsertionTarget').appendChild(fragment);

A proof-of-concept code is as follow:

// Since we are creating so many `<div>` elements
// It helps to further abstract its logic into another function
function createDivElement(classes, text) {
  const div = document.createElement('div');
  
  if (classes.length)
    div.classList.add(...classes);
  
  if (text)
    div.innerText = text;
  
  return div;
}

// Call this whenever you want to create a new card
function createCard(i) {
  const colCountry = createDivElement(['col-12', 'p-1'], 'Country');
  const colState = createDivElement(['col-3', 'p-1'], 'State');
  const colCity = createDivElement(['col-4', 'p-1'], 'City');
  
  const row = createDivElement(['row']);
  row.appendChild(colCountry);
  row.appendChild(colState);
  row.appendChild(colCity);
  
  const cardBody = createDivElement(['card-body']);
  cardBody.appendChild(row);
  
  const image = document.createElement('img');
  image.alt = 'img';
  // Proof-of-concept image source, you can ignore this!
  image.src = `https://placehold.it/100x50?text=Image%20${i+1}`;
  
  const imageLink = document.createElement('a');
  imageLink.href = '#';
  imageLink.appendChild(image);
  
  const card = createDivElement(['card', 'my-3', 'mx-1']);
  card.appendChild(imageLink);
  card.appendChild(cardBody);
  
  const outer = createDivElement(['col-12', 'col-md-4']);
  // outer.style.display = 'none';
  outer.appendChild(card);
  
  return outer;
}

// Create new document fragment
const fragment = new DocumentFragment();

// In each iteration of the loop, insert the new card element into fragment
for (let i = 0; i < 5; i++) {
  const el = createCard(i);
  fragment.appendChild(el);
}

// When you're done generating the entire set of elements
// You can then insert the fragment into your DOM (finally!)
document.querySelector('#app').appendChild(fragment);
<div id="app"></div>

Sign up to request clarification or add additional context in comments.

Comments

1

Performance impact is not great for 15-20 elements and that markup alone.

However if you can prove it's slow, know that strings are faster. So a faster approach is this:

  1. Store the markup template as a string
  2. Create a string with the final markup - it can repeat the template as many times as it needs, obviously filled with that
  3. Insert markup in target node

Here is how that would look like:

const products = [{ title: 'gearbox' }, { title: 'drive shaft' }, { title: 'spark plug'}]
const myTemplate = '<div class="product">{title}</div>'
const finalMarkup = products.map(({ title }) => myTemplate.replace('{title}', title))

document.getElementId('targetNode').innerHtml = finalMarkup

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.