This can be on webqueryopen, or you can have a document container for the HTML (to save strain on the server every time a document is opened), and use dblookup/computed text to get the HTML. For menus that show on every page, I think dblookups/container-documents are the best solution. For HTML-reports that are per page, use a Richt-text field..
With a text-field, you can have max 32k data per field, or 64k data if it is a multivalue-field (someone correct me if I'm wrong). With multi-level menus and big tables, you can quickly reach the limit of the field. What I used to do, was to put the HTML in a Rich-text field, and using an iframe or Ajax to get the data.
A simpler solution is to use multiple fields to contain the data. If the data exceeds 32k, simply continue pumping out data to another field.
Pseudocode
If Len(strHTML > 30000) Then
doc.replaceItemValue( "field"+Cstr(counter) ), strHTML )
strHTML = ""
counter = counter + 1
End If
If you use a WQO-agent, have multiple computed for display fields, and make sure they're pass through html. If you don't want to want to generate the HTML every time, you can stamp the document with a date, use computed when composed, or editable fields, and only generate the HTML if something has changed/a day has passed/etc.
If you use a container document, make multiple computed texts with one dblookup each, as there is 32k (?) max length of text a computed text can represent.
Here's a demo-db: link
Open the bigfield-form on the web to see the generated table (WQO-agent is "generatetable").
Screenshot of the form (i generate 2000 rows on WQO):