0

I've created a simple program, that gets data from a database and displays it in an Instagram post fashion. Everything is working so far, but I hoped to find a more compact way to generate this type of structure. I am using the document.createElement() method to do the job, but the code is very repetitive. Are there other, more prefered ways to accomplish this?

Div structure

My code using document.createElement();

//create post wrapper
newPostWrapper = document.createElement("div");
newPostWrapper.className = "postWrapper";
imageContainer.appendChild(newPostWrapper);

//create image imageamountWRAPPER
newImageAmountWrapper = document.createElement("div");
newImageAmountWrapper.className = "imageamountWRAPPER";
newPostWrapper.appendChild(newImageAmountWrapper);

//create image count
newImageCount = document.createElement("div");
newImageCount.className = "imageamountCOUNT";
newImageCount.innerHTML = "1/2";
newImageAmountWrapper.appendChild(newImageCount);

//create detailsWrapper
newDetailsWrapper = document.createElement("div");
newDetailsWrapper.className = "imageDETAILSWRAPPER";
newPostWrapper.appendChild(newDetailsWrapper);

//createDATEwrapper
newDateWrapper = document.createElement("div");
newDateWrapper.className = "imagedisplayDATEWRAPPER";
newDetailsWrapper.appendChild(newDateWrapper);

//create date
newDate = document.createElement("p");
newDate.className = "imagedisplayDATE";
newDate.innerHTML = imageDate;
newDateWrapper.appendChild(newDate);

//create image wrapper
newImageWrapper = document.createElement("div");
newImageWrapper.className = "imagedisplayWRAPPER";
newImageWrapper.id = "imageWrapper" + i;
newDetailsWrapper.appendChild(newImageWrapper);


//create image
newImage = document.createElement("img");
newImage.className = "imagedisplayIMAGE";
newImage.src = "../../uploads/" + profileContent[i][0];
newImage.id = "img" + i;
newImage.setAttribute('contentID', profileContent[i][1]);
newImageWrapper.appendChild(newImage);


//create descriptionWRAPPER
newDescriptionWrapper = document.createElement("div");
newDescriptionWrapper.className = "descriptionWRAPPER";
newPostWrapper.appendChild(newDescriptionWrapper);

//add description
newDescription = document.createElement("p");
newDescription.className = "description";
newDescription.innerHTML = "A new image for the best community ever!"
newDescriptionWrapper.appendChild(newDescription);

Thanks in advance

  • 1
    repetitive code? => functions. It should be an instinctive reaction for a programmer. – trincot Jun 30 '20 at 18:53
  • I've thought about that, but the blocks are not identical. Building a function that creates a block based on parameters would be even more confusing. Do you have another idea? – OneSemicolon Jun 30 '20 at 19:16

3 Answers3

1

I suggest any templating mechanism. There are a lot of JS templating engines such as Angular, React or Vue. If you want to do it just using pure JS, then here is an example what could be done inside the loop,

let imageDate = '2020-07-01';
let imgSrc = 'https://live.staticflickr.com/4561/38054606355_26429c884f_b.jpg';
let i = 4; // get it from the loop.
let myContentID = 1234;

let markup = `
<div class="imageContainer">
    <div class="postWrapper">
        <div class="imageamountWRAPPER">
            <div class="imageamountCOUNT">1/2</div>
        </div>
        <div class="imageDETAILSWRAPPER">
            <div class="imagedisplayDATEWRAPPER">
                <div class="imagedisplayDATE">${imageDate}</div>
                <div class="imagedisplayWRAPPER" id="imageWrapper${i}">
                    <img id="img${i}" contentID="${myContentID}" class="imagedisplayIMAGE" src="${imgSrc}" alt="">
                </div>
            </div>
        </div>
        <div class="descriptionWRAPPER">
            <p class="description">A new image for the best community ever!</p>
        </div>
    </div>
</div>
`;

document.body.innerHTML = markup;
Max Carroll
  • 4,441
  • 2
  • 31
  • 31
Sohel Aman
  • 482
  • 4
  • 6
0

Personally I think it's easier to scale and modify a solution based on : The Content Template element or use Template literals (Template strings) to create the interfaces. Using the DOM API to create and manipulate nodes is usually very verbose and repetitive, for example right now I am trying to create an example of the case that you present using and template strings but since you do not present the data where the divs are generated, I must see the code you show to know how the interface is built and it is quite slow. On the other hand, if you used one of the solutions that I have listed, I would know more quickly which element is the child of another.

I work on the example, I hope to update in a moment

Mario
  • 4,784
  • 3
  • 34
  • 50
  • Content Template element is new to me. I'll give it a try. – OneSemicolon Jun 30 '20 at 20:34
  • for me it is relatively new too, I found it reading this article https://www.netlify.com/blog/2020/04/13/learn-how-to-accept-money-on-jamstack-sites-in-38-minutes/ look in the section Use HTML templates to display asynchronously loaded JSON data – Mario Jun 30 '20 at 22:50
0

You can essentially choose between using .innerHTML and assigning the whole content as HTML, or using the createElement method for each element.

In the first case you can build your string using string concatenation or interpolation with a template literal. Downside is that errors in the markup may not surface immediately.

If you prefer the other approach, then you can go for jQuery and its versatile $ function and append method. If not, you could throw your own utility function.

Here is such a generic function for creating DOM elements:

function elem(tagAndClasses, attribs={}, content=[]) {
    if (Array.isArray(attribs) || typeof attribs == "string") { // attribs was omited as argument
        content = attribs;
        attribs = {};
    }
    let [tag, ...classes] = tagAndClasses.split(".");
    let elem = document.createElement(tag);
    elem.className = classes.join(" ");
    Object.entries(attribs).forEach(([key, value]) => elem.setAttribute(key, value));
    if (Array.isArray(content)) {
        for (let child of content) elem.appendChild(child);
    } else {
        elem.textContent = content;
    }
    return elem;
}

Call it for your example as follows:

imageContainer.appendChild(
    elem("div.postWrapper", [
        elem("div.imageamountWRAPPER" , [
            elem("div.imageamountCOUNT", "1/2")
        ]),
        elem("div.imageDETAILSWRAPPER", [
            elem("div.imagedisplayDATEWRAPPER", [
                elem("p.imagedisplayDATE", imageDate)
            ]),
            elem("div.imagedisplayWRAPPER", { id: "imageWrapper" + i }, [
                elem("img.imagedisplayIMAGE", { 
                    src: "../../uploads/" + profileContent[i][0],
                    id: "img" + i,
                    contentID: profileContent[i][1]
                })
            ])
        ]),
        elem("div.descriptionWRAPPER", [
            elem("p.description", "A new image for the best community ever!")
        ])
    ])
);
trincot
  • 317,000
  • 35
  • 244
  • 286