简体   繁体   中英

Recursive Function to Create DOM Tree String from DOM Object

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>

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"));

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.

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;
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM