Javascript: Add together literal HTML results in loop

Viewed 406

New to JS. All of the questions I could find with similar titles to mine were a bit too complicated for me to understand.

Simply put, I am trying to loop through an array and return one result at the end that essentially adds/concatenates all of the values of the array together.

However, I can't seem to get it to work, as the items in the array are blocks of HTML code, rather than simple strings or numbers. Here's a dumbed-down version.

HTML:

<article class="wrapper"><p>Some text 1.</p></article>
<article>No class.</article>
<article class="wrapper"><p>Some text 2.</p></article>
<article>No class.</article>
<article class="wrapper"><p>Some text 3.</p></article>

I am trying to figure out a way to end up with a template literal of this code:

<article class="wrapper"><p>Some text 1.</p></article>
<article class="wrapper"><p>Some text 2.</p></article>
<article class="wrapper"><p>Some text 3.</p></article>

So that I can inject the entire thing as the innerHTML of another element on a different page.

Started with this JS:

articleArray = document.querySelectorAll('.wrapper').outerHTML;
console.log(articleArray)
// This will return an array with the full HTML of the 3 "wrapper" articles as each item

Then tried to create a loop that will take the full HTML of item 1 on first loop and set it as something like an accumulator value. Then on second loop, it would take the full HTML of item 2 and concatenate it directly to item 1, and that would be the new accumulator value. And so on.

I have far more than 3 items for what I'm applying this to in real life, otherwise I could just do something like articleArray[0] + articleArray[1] + articleArray[2].

I tried a million things, but here were the closest attempts:

  1. Logging
var articleArray = document.querySelectorAll('.wrapper').outerHTML;
for (i = 0; i < searchWrappers.length; i++) {
  searchWrapper = articleArray[i];
  console.log(searchWrapper);
}
// Console log brought back 3 objects, but I need to combine them
  1. Concatenating
var articleArray = document.querySelectorAll('.wrapper').outerHTML;
var searchStr = ''
for (i = 0; i < articleArray.length; i++) {
  itemHTML = articleArray[i];
  fullString = searchStr += itemHTML;
  console.log(fullString);
}
// This did concatenate the 3 items, but only as:
// [object HTMLElement][object HTMLElement][object HTMLElement]
// and not the actual HTML
  1. Concatenating and logging
const articleArray = document.querySelectorAll('.wrapper').outerHTML;
const sum = articleArray.reduce((accumulator, value) => 
  $(accumulator).concat($(value)));
console.log(sum);
// This brought back a normal array of the 3 items again

Any help appreciated!

Edit: Thanks for all your responses! I am going to go through them now and testing your solutions on my real code to see what works best.

5 Answers

This should help you:

function contains(selector, text) {
    var elements = document.querySelectorAll(selector);
    return [].filter.call(elements, function (element) {
        return RegExp(text).test(element.textContent);
    });
}

var articleArray = contains(".wrapper", 'menu');
// var articleArray = contains(".wrapper", /menu/i); // for case-insensitive
let templateLiteral = ``;
articleArray.forEach(function (article) {
    templateLiteral += article.outerHTML + '\n';
})
console.log(templateLiteral);
article {
    display: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<article class="wrapper"><p>Some text 1. menu</p></article>
<article>No class.</article>
<article class="wrapper"><p>Some text 2. menu</p></article>
<article>No class.</article>
<article class="wrapper"><p>Some text 3.</p></article>


</body>
</html>

The basic idea is to loop through each HTML element and get add the outerHTML of the element to the template literal.

Edit: Thanks to this answer, it helps me achieve the desired effect of finding a vanilla js replacement for :contains in jQuery.

You could map the elements to their outer HTML and then join the results.

const htmlString = [...document.querySelectorAll('.wrapper')]
  .map(({ outerHTML }) => outerHTML.trim())
  .join('\n');

console.log(htmlString);
.as-console-wrapper { top: 0; max-height: 100% !important; }

.wrapper { display: none; }
<article class="wrapper">
  <p>Some text 1.</p>
</article>
<article>No class.</article>
<article class="wrapper">
  <p>Some text 2.</p>
</article>
<article>No class.</article>
<article class="wrapper">
  <p>Some text 3.</p>
</article>

You can create template in the following way, via concatenating each article outerHTML:

var articleArray = document.querySelectorAll('.wrapper');
var template = '';

articleArray.forEach(article => template += article.outerHTML);

console.log(template)

Example in jsbin

if you need to check for extra word (for example 'menu') inside each article, you could add the following condition:

articleArray.forEach(article => {
  if (article.innerText.includes('menu')) {
     template += article.outerHTML.trim() 
  }
});

You can also fetch only wrappers and push them in new element to make a template:

let result = document.createElement("div")
document.querySelectorAll('.wrapper').forEach(e =>
  result.insertAdjacentElement("beforeend", e))

console.log(result.innerHTML)
<article class="wrapper">
  <p>Some text 1.</p>
</article>
<article>No class.</article>
<article class="wrapper">
  <p>Some text 2.</p>
</article>
<article>No class.</article>
<article class="wrapper">
  <p>Some text 3.</p>
</article>

Then you can use result.innerHTML to transfer all.

Also see: Element.insertAdjacentElement()

EDIT: You will actuality need to use cloning so wrappers does not get removed from DOM:

let result = document.createElement("div")

document.querySelectorAll('.wrapper').forEach(e => {
  let cln = e.cloneNode(true)
  result.insertAdjacentElement("beforeend", cln)
})

console.log(result.innerHTML)
<article class="wrapper">
  <p>Some text 1.</p>
</article>
<article>No class.</article>
<article class="wrapper">
  <p>Some text 2.</p>
</article>
<article>No class.</article>
<article class="wrapper">
  <p>Some text 3.</p>
</article>

DOMParser is the right tool if you really have:

"the items in the array are blocks of HTML code"

// I have an array of elements as HTML strings:
const elArray = [
  `<article class="wrapper"><p>Some text 1.</p></article>`,
  `<article>No class.</article>`,
  `<article class="wrapper"><p>Some text 2.</p></article>`,
  `<article>No class.</article>`,
  `<article class="wrapper"><p>Some text 3.</p></article>`,
];

// DOMParser is the right tool for the job!
const DOC = new DOMParser().parseFromString(elArray.join(""), "text/html");

// Use selectors as normal!
const ELs = DOC.querySelectorAll(".wrapper");

// DEMO TIME: retrieve elements and finally
// place them somewhere in the app
document.querySelector("#test").append(...ELs);
<div id="test"></div>

If instead you actually have a NodeList of Elements:

// I am trying to figure out a way to end up
// with a template literal!

const ELs = document.querySelectorAll("article.wrapper"); 
const literal = [...ELs].reduce((s, EL) => s + EL.outerHTML, "");
console.log(literal);
<article class="wrapper"><p>Some text 1.</p></article>
<article>No class.</article>
<article class="wrapper"><p>Some text 2.</p></article>
<article>No class.</article>
<article class="wrapper"><p>Some text 3.</p></article>

Depends on what you're actually building, you need to be aware that when taking items from the DOM and converting them to string you might lose any Element Properties or Events (like "click" etc) assigned to those elements.

In such case you might just want to perhaps simply use Element.append(...myNodesList) to move them to your desired new location in the DOM:

// Let's say they have some event listeners already assigned:
document.querySelectorAll(".wrapper").forEach(EL => EL.addEventListener("click", (ev) => {
  console.log(ev.currentTarget.textContent);
}));

// Get your elements
const ELs = document.querySelectorAll("article.wrapper"); 
// And simply move them somewhere else!
document.querySelector("#test").append(...ELs); 
// Click events are preserved!!
#test { padding: 10px; background: #eee; }
<article class="wrapper"><p>CLICK ME! 1.</p></article>
<article>No class.</article>
<article class="wrapper"><p>CLICK ME! 2.</p></article>
<article>No class.</article>
<article class="wrapper"><p>CLICK ME! 3.</p></article>

<div id="test">
  <!-- Let's say you want to insert your .wrapper elements here! -->
</div>

Related