skip to Main Content

I want to dynamically update my page based on HTML fragments that are rendered on the server. In a first simple approach I am simply fetching the fragments from the server and update the target via .innerHTML.

Unfortunately this causes two problems (which are basically the same):

  1. When a user selects any text within the dynamic content, the selection will be cleared. Even, if the selected text did not change. This can be see in the example below: When selecting foo, it will be cleared after an update, but it does not actually change.
  2. Any state that is attached to the elements gets deleted too. In particular the dynamic content of my page has some buttons which will show a loading indicator while the request is processing. It is okay for me, if the indicator is gone when the content changes, but the state being cleared every second makes the indicator useless.

My actual question is, whether there is an easy way to only update the elements that changed. Especially when when elements can be added, removed or reordered.

let counter = 0;

setInterval(() => {  
  counter++;
 
  // assume this is fetched from the server
  content = '<ul><li>foo</li><li>'+counter+'</li><li>bar</li></ul>'; 
  
  document.getElementById("dynamic-content").innerHTML = content;
}, 1000);
<div>
  <h1>my page</h1>
  <div id="dynamic-content">
      <ul>
        <li>foo</li>
        <li>0</li>
        <li>bar</li>
      </ul>
  </div>
</div>

2

Answers


  1. Chosen as BEST ANSWER

    I found a simple library called idiomorph, which does pretty much what I want. Generally there are multiple library for this use case and they describe themselves as DOM "morphing" libraries.

    So instead of

    document.getElementById("dynamic-content").innerHTML = content;
    

    I just have to do

    Idiomorph.morph(document.getElementById("dynamic-content"), content, {morphStyle:'innerHTML'});
    

    Additionally I have to put id tags on every element, but this is easy in my case.


  2. Depends upon what you want to replace, and knowing how to target that. Here I did a totally artificial example of a partial replacement.

    • I would do this in a promise not an interval
    • You need to know what you want to do – here I show how to pick a sub-section out of what was (fake) returned and use that for example.
    let counter = 0;
    
    setInterval(() => {
      counter++;
      const newDiv = document.createElement("div");
    
      // assume this is fetched from the server
      let content = '<ul><li>foo</li><li class="counter-thing"><span class="new-count">' + counter + '</span></li><li>bar</li></ul>';
      newDiv.innerHTML = content;
      let newc = newDiv.querySelector(".counter-thing").innerHTML;
      console.log(newc);
      let dy = document.querySelector("#dynamic-content >ul");
      let x = dy.querySelector("li:nth-of-type(2)");
      console.log(x);
      x.innerHTML = newc;
      // another way to put some text in (be careful of HTML here)
      document.querySelector('.example-two').dataset.content = counter;
    }, 3000);
    #dynamic-content>ul {
      list-style: none;
      display: block;
      margin: 1rem;
      padding: 0;
    }
    
    .foo-me {
      border: solid 1px #00FF00;
    }
    
    .new-count {
      background-color: #FF44FF44;
      padding: 1rem;
      margin: 1rem;
      border: 1px solid red;
    }
    
    .example-two[data-content]:before {
      content: attr(data-content);
    }
    <div>
      <h1>my page</h1>
      <div id="dynamic-content">
        <ul>
          <li class="foo-me">foo</li>
          <li>0</li>
          <li>bar</li>
        </ul>
      </div>
    </div>
    
    Fake out another way to replace content:
    <div class="example-two" data-content="Some content goes here."></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search