GrapesJS / grapesjs

Free and Open source Web Builder Framework. Next generation tool for building templates without coding
https://grapesjs.com
BSD 3-Clause "New" or "Revised" License
22.15k stars 4.02k forks source link

BUG: HTML elements ids are incremented on different pages loaded in projectData #4837

Open quentin-bettoum opened 1 year ago

quentin-bettoum commented 1 year ago

GrapesJS version

What browser are you using?

Firefox 109

Reproducible demo link

https://grapesjs.com/demo.html

Describe the bug

Hello,

I noticed that when I load multiple pages in the projectData that have the same id used in the HTML, grapes will automatically increment the id on every page even though these are different pages.

To demonstrate this issue, you will find a piece of code below where I init grapesjs with a projectData containing two pages using identical ids on some HTML tags.

On the resulting HTML list, you can see the ids like body and main-title are incremented to body-2 and main-title-2 on the second page.

const editortest = grapesjs.init({
  headless: true,
  projectData: {
    pages: [
      {
        "frames": [
          {
            "component": {
              "type": "wrapper",
              "stylable": [
                "background",
                "background-color",
                "background-image",
                "background-repeat",
                "background-attachment",
                "background-position",
                "background-size"
              ],
              "attributes": {
                "id": "body"
              },
              "components": [
                {
                  "tagName": "section",
                  "components": [
                    {
                      "tagName": "h1",
                      "type": "text",
                      "attributes": {
                        "id": "main-title"
                      },
                      "components": [
                        {
                          "type": "textnode",
                          "content": "This is a simple title"
                        }
                      ]
                    },
                    {
                      "type": "text",
                      "components": [
                        {
                          "type": "textnode",
                          "content": "This is just a Lorem text: Lorem ipsum dolor sit amet"
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          }
        ],
        "id": "ljc0blhC1ZeiCYyY"
      },
      {
        "frames": [
          {
            "component": {
              "type": "wrapper",
              "stylable": [
                "background",
                "background-color",
                "background-image",
                "background-repeat",
                "background-attachment",
                "background-position",
                "background-size"
              ],
              "attributes": {
                "id": "body"
              },
              "components": [
                {
                  "tagName": "section",
                  "components": [
                    {
                      "tagName": "h1",
                      "type": "text",
                      "attributes": {
                        "id": "main-title"
                      },
                      "components": [
                        {
                          "type": "textnode",
                          "content": "This is another title"
                        }
                      ]
                    },
                    {
                      "type": "text",
                      "components": [
                        {
                          "type": "textnode",
                          "content": "Some other text"
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          }
        ],
        "id": "1zKkQnGVzy8nKXKE"
      }
    ]
  }
})

let pages = editortest.Pages.getAll().map(page => {
  const component = page.getMainComponent();
  return editortest.getHtml({ component })
});

console.log(pages)

The pages result:

[
  "<body id=\"body\"><section><h1 id=\"main-title\">This is a simple title</h1><div>This is just a Lorem text: Lorem ipsum dolor sit amet</div></section></body>",
  "<body id=\"body-2\"><section><h1 id=\"main-title-2\">This is another title</h1><div>Some other text</div></section></body>"
]

Code of Conduct

artf commented 1 year ago

Even with multiple pages all components have to maintain a unique id across whole project. I'd not expect the editor to output this so I'm wondering if you edited the JSON on purpose or you have reproducible steps to have this outcome?

quentin-bettoum commented 1 year ago

My use case might explain how I had this result.

I am building a CMS using GrapesJS as a Page Builder. To create each page, I chose to load Grapes in "single page mode", if that makes sense. This is to avoid sending too much data to the client on a project containing a lot of pages. Then, to generate the final website, the goal is to load Grapes in headless mode serverside (using NodeJS) to merge all the pages and styles.

I can live without ids at the moment, this is just something I noticed in my multiple tries. In the long term, maybe it could make sense for Grapes to use some data attributes (something like data-grapes-id) as unique identifiers to leave the id free for some frontend scripts.

I might open a discussion where we could speak more about this CMS I'm building. I'd be glad to have your opinions and advices on my design choices.

artf commented 1 year ago

Then, to generate the final website, the goal is to load Grapes in headless mode serverside (using NodeJS) to merge all the pages and styles.

I'm wondering if it would make sense to you generating each page separately

quentin-bettoum commented 1 year ago

At first, I wanted to generate all the pages together to have a single CSS output for all the pages. But now I'm trying another solution for the styles.

So for the id problem, generating each page separately could be a solution. Thanks for the idea.

vizardkill commented 1 year ago

I have a question regarding the StoreManager, if users start creating many pages, at what point does performance start to be lost? , taking into account that you must store the assets, and the styles and taking into account how you could store this information in a database without affecting the load of the API when processing so much information

lexoyo commented 1 year ago

Hello In Silex v3 I encounter this bug too and I have a use case for which it is a real issue unfortunately We have a menu using a checkbox and a label to open/close it so we need to have an id on the checkbox (used in the for attribute of the label) We use symbols to have the menu on all pages so it has to be the same id...

bgrand-ch commented 11 months ago

Grapes to use some data attributes (something like data-grapes-id) as unique identifiers to leave the id free for some frontend scripts.

I like this idea 💯

elfalem commented 1 month ago

I also have a use case where the content is initially generated outside of the editor so different pages can have elements with the same id.

Even with multiple pages all components have to maintain a unique id across whole project.

Is it possible to relax this constraint so that ids are namespaced by pages?