-1

I'm working on a cargocollective based system and trying to change the existing project labels using javascript. right now when users enter the address there are 1 or 2 seconds where the labels transform from one stage to another, so the user first see:

project label - 1'st stage

and then after a few seconds see:

project label - 2'nd stage

Is there any way to modify it so the users will not see the change when page load finished? this is my current code:

<script>
document.addEventListener('DOMContentLoaded', (event) => {
  window.setTimeout(() => {
      let titles = document.querySelectorAll(".thumbnails .title span");
    let titlesArray = Array.from(titles);
    let tArray = [titlesArray.length];
    titlesArray.forEach(el => {
      let titleSplit =[];
      titleSplit = el.textContent.split('---');
      for (let i = 0; i < tArray.length; i++)
      {
        el.textContent = "";
        for (let j = 0; j < titleSplit.length; j++)
        {
          el.innerHTML += `${titleSplit[j]}</br>`;
        }
      }
});
  }, 400);
});
</script>

A few things I need to mention:
1. The system allows inner js scripts only, it does not allow HTML editing, which means only code that is inserted between script tags
2. The solution has to be in javascript for future uses.
3. The "---" is for cosmetic use only and will replace with an SVG image.
4. script tags location is in the middle of the file. also, there is a lot of content between the script and the end of the page. 5. for view-source: webpage in context

Eden Sharvit
  • 765
  • 11
  • 14

2 Answers2

2

Don't wait for DOMContentLoaded, and don't put the functionality into a setTimeout. Instead, put the <script> right after the final element that the script depends on - the last .thumbnails .title span:

<div class="thumbnails">
  <div class="title">
    <span>
      ...
    </span>
  </div>
</div>
<script>
// Put the code here
</script>

This way, the change should occur immediately, before the page has even rendered for the user, so it'll look as desired the first time they see it.

With regards to the code, let tArray = [titlesArray.length]; doesn't make much sense - you're creating an array with a single element, a number, which you proceed to iterate over in a nested loop. If you just want to replace the ---s with a blank line, and the spans only contain plain text, then use:

for (const span of document.querySelectorAll(".thumbnails .title span")) {
  span.innerHTML = span.innerHTML.split('---').join('<br><br>');
}

<div class="thumbnails">
  <div class="title">
    <span>
      foobar
      ---
      barbaz
    </span>
  </div>
</div>
<script>
for (const span of document.querySelectorAll(".thumbnails .title span")) {
  span.innerHTML = span.innerHTML.split('---').join('<br><br>');
}
</script>

(you need two <br>s for there to be two newlines between the two words, so that a single empty line appears in the rendered output)


If you can't alter the position of the <script> tag, and there's a lot of content between the script and the end of the HTML, and the script runs before the elements you need exist, then you won't be able to select the elements immediately when the script runs, nor can you wait for DOMContentLoaded for them to exist, since it may take a noticeable amount of time between when the required elements appear and when the DOM is fully parsed. The only alternative is to attach a MutationObserver to the document, which can run a callback whenever a new node gets inserted. If a new node is one of the ones you want to alter, alter it. This ensures that the change occurs as soon as possible, before rendering:

<script>
new MutationObserver((mutations) => {
  for (const mutation of mutations) {
    for (const node of mutation.addedNodes) {
      if (node.nodeType === 1 && node.matches('.thumbnails .title span')) {
        node.innerHTML = node.innerHTML.split('---').join('<br><br>');
      }
    }
  }
})
  .observe(document.body, { childList: true, subtree: true });
</script>
<div class="thumbnails">
  <div class="title">
    <span>
      foobar
      ---
      barbaz
    </span>
  </div>
  <div class="title">
    <span>
      foobar
      ---
      barbaz
    </span>
  </div>
</div>

It could be made less expensive - for example, only attach the deep observer once the container for all the thumbnails exists, and deattach it once the container has ended - but more information about the DOM structure would be needed for that.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • You don't need to change any of the tangible elements, you just need to move the position of your script - you said you could alter the script, right? – CertainPerformance Mar 10 '20 at 00:22
  • If you can't move the position of the script, then if the script comes after the required elements, the same code will work. Press "Run code snippet" for a live demo. – CertainPerformance Mar 10 '20 at 00:23
  • I can't edit the HTML file using any elements nor classes or ids, It is a close system that why although your solution is good It is still not applicable in my case. The ```let tArray = [titlesArray.length];``` should be variable instead - thanks for the heads-up. The "---" will eventually be replaced with an SVG or PNG image. – Eden Sharvit Mar 10 '20 at 00:24
  • No classes nor IDs are being added in my code. I'm just using the same selector that you said works in your code - the `.thumbnails .title span`. – CertainPerformance Mar 10 '20 at 00:25
  • I cant alter the existing HTML. only to add an inline script to it. – Eden Sharvit Mar 10 '20 at 00:27
  • Again, like I said, I'm not editing any of the tangible elements. You don't have to edit any of the HTML other than the inline script. – CertainPerformance Mar 10 '20 at 00:28
  • I know, and I've got to mention that I tried the solution you recommend me. and as I mentioned this is a close system and therefore it responds to me with an error message when I'm trying to enter code that is not contained inside script tags. – Eden Sharvit Mar 10 '20 at 00:33
  • The code that I changed: `for (const span of document.querySelectorAll(".thumbnails .title span")) { span.innerHTML = span.innerHTML.split('---').join('

    '); }` *is* all inside a `
    – CertainPerformance Mar 10 '20 at 00:34
  • I tried the this code right now with no success. I don't know if it is important to mention but it might help for clarification: Cargocollective system uses handlebars.js, there is an option over there to edit the HTML files but they only give permission to do so by adding js code inside script tags. The code u gave should work, but if I'm adding only the code, the data is changing before it initialized. probably because in this system you cant determine the script tags location. at least this is what I think... – Eden Sharvit Mar 10 '20 at 00:48
  • Where is the ` – CertainPerformance Mar 10 '20 at 00:49
  • at the bottom of the page – Eden Sharvit Mar 10 '20 at 00:50
  • Then the script should work as is - since the `.thumbnails .title span` will be *above* in the HTML, they'll be able to be selected immediately by the script. – CertainPerformance Mar 10 '20 at 00:50
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/209342/discussion-between-eden-sharvit-and-certainperformance). – Eden Sharvit Mar 10 '20 at 00:55
0

I've managed to fix my issue and decide to post it here for others who may need it:
Some clarifications before:

  1. As I mentioned before CargoCollective allows adding inner js scripts via the option of HTML editing only (code must be inserted between script tags).
  2. The "---" placeholder (in my original question) was removed since the designer asked me to, so the solution doesn't include it.
  3. The specific helper function 'thumbnails_render_complete' wasnt mention in the documentation of CargoCollective, I've got it from their support during a chat session.

Hope it will help someone

<script>
  Cargo.Event.on('thumbnails_render_complete', function(pid) {
    let titles = document.querySelectorAll(".thumbnails .title span");
    let titlesArray = Array.from(titles);
    let tArray = titlesArray.length;
    titlesArray.forEach(el => {
      let titleSplit =[];
      titleSplit = el.textContent.split('-');
      for (let i = 0; i < tArray; i++) {
        el.textContent = "";
        for (let j = 0; j < titleSplit.length; j++) {
          if ( j === 0 ) {
            el.innerHTML += `${titleSplit[j]}</br>`;
          }
          else {
            let subtext = titleSplit[j];
            el.innerHTML += `${subtext}</br>`;
          }
        }
      }
    });
  });
</script>
Eden Sharvit
  • 765
  • 11
  • 14