Search code examples
javascriptdocumenttruncateselectors-api

Truncating titles from reddit API using multi-clamp.js


I'm trying to use (https://www.npmjs.com/package/multi-clamp)[multi-clamp.js] to clamp/truncate my titles that are being returned from the reddit api.

However, it is only working on the 1st retuned title rather than all of the titles. How would I ensure that the function runs on all of the titles returned from the api and not just the 1st?

const reddit = "https://www.reddit.com/r/upliftingnews.json?raw_json=1&limit=10"

async function getData() {
    try {
        let response = await fetch(reddit);
        let data = await response.json()
        return data;
    } catch (error) {
        console.log("error", error);
    }
}

getData()
    .then(data => data.data.children)
    .then(data => data.map(post => ({
        source: post.data.domain,
        author: post.data.author,
        link: post.data.url,
        img: (typeof (post.data.preview) !== 'undefined') ? post.data.preview.images[0].source.url : null,
        title: post.data.title,
    })))
    .then(data => data.map(render))

const app = document.querySelector('#app');

const render = post => {
    //console.log(post.data);
    const node = document.createElement('div');
    node.classList.add('news-item', `news-item--${ post.class }`);
    node.innerHTML = `
    <a class="news-item-link" href="${post.link}">
        <div style="background-image: url('${post.img}')" class="news-item-bg"></div>
        <div class="news-item-content">
            <h3 class="news-item-source">${post.source}</h3>
            <h2 class="news-item-title mb-2">${post.title}</h2>
        </div>
    </a>`;
    app.appendChild(node);

    new MultiClamp(document.querySelector('.news-item-title'), {
        ellipsis: '...',
        clamp: 2
    });
}

new MultiClamp.. is where the clamping runs on the title selector but it's only clamping the first returned title, rather than all of them.

How do I get it to run on all titles?


Solution

  • document.querySelector returns only the first element that matches the selector. Since you are executing it on the whole document, it will always get the first title that you appended to the document, and create many new MultiClamps on it.

    Instead, you need to select the one new title element in your new node:

    function render(post) {
        const node = document.createElement('div');
        node.classList.add('news-item', `news-item--${ post.class }`);
        node.innerHTML = `
        <a class="news-item-link" href="${post.link}">
            <div style="background-image: url('${post.img}')" class="news-item-bg"></div>
            <div class="news-item-content">
                <h3 class="news-item-source">${post.source}</h3>
                <h2 class="news-item-title mb-2">${post.title}</h2>
            </div>
        </a>`;
        app.appendChild(node);
    
        new MultiClamp(node.querySelector('.news-item-title'), {
    //                 ^^^^
            ellipsis: '...',
            clamp: 2
        });
    }
    

    Btw, using innerHTML with unescaped interpolation values opens up your application for XSS attacks. Better build the entire node contents using the DOM API as well.