0

I'm currently struggling with trying to build out a function that should take an input object from parsing HTML5 (node-html5-parser) and I need to process all tags and children tags, with their content to output a string of xml. The issue I'm running into is how to get a recursive function (or any function) to properly maintain the HTML5 tag order and outputting the content.

Example:

<div><span>My Content</span></div>

With the node-html5-parser, I get the following when it parses that:

rootElem {
    childNodes: [
        {
            tagName: 'div',
            childNodes: [
                {
                    tagName: 'span',
                    childNodes: [
                        {
                             rawText: 'My Content'
                        }
                    ],
                }
            ]
        }
    ]
}

I thought a simple DFS recursive algorithm could be used to build up a string, but I can't get it to work correctly.

const DOMRootObj = parse(this.htmlStr);

const processedData = this.processContent(DOMRootObj);
 processContent(root: any): string {
    if (root && !root.childNodes.length) {
      return root.rawText;
    } else {
      for (const childNode of root.childNodes) {

        const str = this.processContent(childNode);

        const { tagName } = childNode;

        if (tagName) {
          this.outputStr += `<${tagName}>${str}</${tagName}>`;
        }

      }
    }


  }

from this HTML that's parsed by the parse() function: (object is as above)

<div><span>My Content</span></div>

This ends up outputting:

<span>undefined</span><div>undefined</div>

but It should output:

<div><span>My Content</span></div>
2
  • Please edit your question to include the code you've written so far. Commented Oct 8, 2019 at 20:35
  • So what is the expected XML output for that example? Commented Oct 8, 2019 at 21:02

3 Answers 3

2

Not sure which XML format you expect, but the recursive call is not that hard to implement:

function toString(node, name="root") {
    let tag = typeof name === "string" ? name : node.tagName;
    return  tag ? `<${tag}>${(node.childNodes||[]).map(toString).join``}</${tag}>`
        : (node.rawText || "");
}

// Sample:
let rootElem = {
    childNodes: [{
        tagName: 'div',
        childNodes: [{
            tagName: 'span',
            childNodes: [{
                 rawText: 'My Content'
            }],
        }]
    }]
};

console.log(toString(rootElem, "root"));

Sign up to request clarification or add additional context in comments.

2 Comments

This worked great! Thanks! That use of map is very interesting. Definitely hadn't considered it before as a free way to do DFS. To Note, in Typescript I had to call .map(this.toString, this)
Good to hear. You need map(this.toString, this) when toString is defined as a method. This is not specific to Typescript.
0

Ive got a few days some same kind of problem inside a browsere. I want to walk over a dom tree and execute a function with given parameters on the nodes. In my case i want to reassign the element id if any. Thats why the function is named nodeinit. Anyhow

             function nodeinit(node,dataset){console.log(node);}  

             function shadowWalker(node, nodeinit, dataset) {
                nodeinit(node, dataset);
                if (node) {
                    console.log(node.id);
                    if (node.childNodes.length > 0) {
                        node = node.firstChild; //needed to init while loop
                        while (node) {
                            shadowWalker(node, nodeinit, dataset);
                            node = node.nextSibling;
                        }
                    }
                }
            }

So its called with the node to start of. Nodeinit is a function and dataset is the parameter object for the nodeinit function (some kind of presets). Some similar answers you can find here by looking for transverse dom tree. Just as a idea or starting point.

Comments

0

Unpolished code, but you can get the ideia. You can add more attributes like onclick and tabindex simply by repeating the pattern. Although I don't think putting everything on the generator a good idea (you can lose track of what's being generated).

fillHeaderCurrenciesList = () => {
    // Generated structure example
    // <div
    //     id='currency-1'
    //     class='currency currency--hoverable'
    //     onclick='selectMainCurrency(this.id)'
    // >
    //     <div class='currency__name'>Bitcoin</div>

    //     <div class='currency__icon'>
    //         <img src='assets/img/icon-coin.svg' alt='LG Bank' />

    //         <img src='assets/img/icon-btc.svg' alt='LG Bank' />
    //     </div>

    //     <div class='currency__balance'>
    //         <span class='currency__title'>Balance</span>

    //         <div class='currency__value'>
    //             <span>Exemplo1 | R$ 234.342.367,90</span>
    //         </div>
    //     </div>
    // </div>;

    let currenciesList = document.getElementById('currencies-list');

    for (let i = 0; i < currencyData.length; i++) {
        const currency = currencyData[i];

        let currencyTree = {
            tagName: 'div',

            id: `currency-${i}`,
            class: ['currency', 'currency--hoverable'],
            onClick: function () {
                selectMainCurrency(this, 'currencies-main-currency');
            },

            onKeyDown: function () {
                keyDownEnter(
                    selectMainCurrency(this, 'currencies-main-currency')
                );
            },

            tabIndex: '0',

            children: [
                {
                    tagName: 'div',
                    class: ['currency__name'],
                    text: currency.name,
                },

                {
                    tagName: 'div',
                    class: ['currency__icon'],

                    children: [
                        {
                            tagName: 'img',
                            src: currency.icon.src,
                            alt: currency.icon.alt,
                        },

                        {
                            tagName: 'img',
                            src: currency.iconName.src,
                            alt: currency.iconName.alt,
                        },
                    ],
                },

                {
                    tagName: 'div',
                    class: ['currency__balance'],

                    children: [
                        {
                            tagName: 'span',
                            class: ['currency__title'],
                            text: 'Balance',
                        },

                        {
                            tagName: 'div',
                            class: ['currency__value'],

                            children: [
                                {
                                    tagName: 'span',
                                    text: currency.balance,
                                },
                            ],
                        },
                    ],
                },
            ],
        };

        currenciesList.appendChild(DOMListItemGenerator(currencyTree));
    }
};

const currencyData = [
    {
        name: 'bitcoin',
        icon: {
            src: 'assets/img/icon-coin.svg',
            alt: 'LG Bank',
        },
        iconName: {
            src: 'assets/img/icon-btc.svg',
            alt: 'LG Bank',
        },
        quotation: '',
        balance: 'Exemplo1bitcoin | R$ 234.342.367,90',
    },
    {
        name: 'ethereum',
        icon: {
            src: 'assets/img/icon-coin.svg',
            alt: 'LG Bank',
        },
        iconName: {
            src: 'assets/img/icon-btc.svg',
            alt: 'LG Bank',
        },
        quotation: '',
        balance: 'Exemplo2ethereum | R$ 234.342.367,90',
    },
    {
        name: 'ethereum',
        icon: {
            src: 'assets/img/icon-coin.svg',
            alt: 'LG Bank',
        },
        iconName: {
            src: 'assets/img/icon-btc.svg',
            alt: 'LG Bank',
        },
        quotation: '',
        balance: 'Exemplo2ethereum | R$ 234.342.367,90',
    },
    {
        name: 'ethereum',
        icon: {
            src: 'assets/img/icon-coin.svg',
            alt: 'LG Bank',
        },
        iconName: {
            src: 'assets/img/icon-btc.svg',
            alt: 'LG Bank',
        },
        quotation: '',
        balance: 'Exemplo2ethereum | R$ 234.342.367,90',
    },
    {
        name: 'teste',
        icon: {
            src: 'assets/img/icon-coin.svg',
            alt: 'LG Bank',
        },
        iconName: {
            src: 'assets/img/icon-btc.svg',
            alt: 'LG Bank',
        },
        quotation: '',
        balance: 'Exemplo3teste | R$ 234.342.367,90',
    },
];

fillHeaderCurrenciesList();


DOMListItemGenerator = (inputTree) => {
    let tree = Object.entries(inputTree);
    let item;
    let output;

    for (let i = 0; i < tree.length; i++) {
        const branch = tree[i];

        if (branch[0] === 'tagName') {
            output = document.createElement(branch[1]);
        }

        if (branch[0] === 'id') {
            output.setAttribute('id', branch[1]);
        }

        if (branch[0] === 'tabIndex') {
            output.setAttribute('tabindex', branch[1]);
        }

        if (branch[0] === 'class') {
            for (const classItem of branch[1]) {
                output.classList.add(classItem);
            }
        }

        if (branch[0] === 'src') {
            output.src = branch[1];
        }

        if (branch[0] === 'alt') {
            output.alt = branch[1];
        }

        if (branch[0] === 'text') {
            output.textContent = branch[1];
        }

        if (branch[0] === 'onClick') {
            output.onclick = branch[1];
        }

        if (branch[0] === 'onKeyDown') {
            output.onkeydown = branch[1];
        }

        if (branch[0] === 'children' && branch[1].length > 0) {
            for (let j = 0; j < branch[1].length; j++) {
                const children = branch[1][j];
                item = DOMListItemGenerator(children);
                output.appendChild(item);
            }
        }
    }

    return output;
};

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.