Misaligned header when using scrollX and margin:auto

Misaligned header when using scrollX and margin:auto

mwoutsmwouts Posts: 19Questions: 3Answers: 0

Hi there,

I have recently added a "Streamlit" component to ITables, that let Python users render their DataFrames as interactive datatables in that context - see the demo here: https://itables.streamlit.app/

For the Streamlit component I've had to set scrollX=True by default as the tables are rendered with an iframe that, unlike Jupyter Notebook outputs cells, don't have an horizontal scroll bar.

This mostly works, but when I try to center the table using margin:auto;table-layout:auto;width:auto;caption-side:bottom for the style, I get a centered table but the header remains on the left, like in the screenshot below.

Do you see how to fix that? Thanks!

This question has an accepted answers - jump to answer


  • mwoutsmwouts Posts: 19Questions: 3Answers: 0

    NB: to really get margin:auto in the app above, it's necessary to set it as the beginning or final condition (or to use a space), because the app currently replaces ;margin:auto; with ;margin:0; (that was an effort to no expose the users to this issue :smile: )

  • kthorngrenkthorngren Posts: 20,796Questions: 26Answers: 4,862

    A common problem is that Datatables needs style="width:100%" set on the table tag, as shown in this example to help calculate column sizes. Looks the width:auto; sets the table width at 844px no matter the page width. Inspecting the table shows this:

    <table class="display nowrap dataTable" aria-describedby="DataTables_Table_0_info" style="table-layout: auto; margin: 0px; caption-side: bottom; width: 844px;">...</table>

    Not sure how to set this specifically on the table tag in your environment. Possibly setting ``width:100%;` will work.


  • mwoutsmwouts Posts: 19Questions: 3Answers: 0

    Thank you Kevin!

    I experimented with the table style set to margin:auto;table-layout:auto;width:100%;caption-side:bottom (through the "style" text input in the app) but I am afraid I still get the issue (I will try harder this week-end!)

  • kthorngrenkthorngren Posts: 20,796Questions: 26Answers: 4,862

    What you have highlighted is the div. Look at the table tag instead:

    Not sure if the test case you linked to is updated but it still shows a width of 844px.


  • kthorngrenkthorngren Posts: 20,796Questions: 26Answers: 4,862
    edited June 6

    I looked at this more and realized this is a sample app from the developers of iTables. Looking at it further it seems that no matter what the width setting in the Style input is set to the table tag has width: 844px set. For example:

    I would check with the iTables developers about the column width issue when using scrollX. Its unclear what exactly they are doing to setup the HTML and Datatables environment. Looks like you can ask about this here:


  • mwoutsmwouts Posts: 19Questions: 3Answers: 0

    Thanks Kevin! BTW I do maintain ITables and that's why I am trying to get this right :blush:

    The style in the text box is passed verbatim to the table (well except for the replacement of ;margin:auto; with ;margin:0; which I will remove when we solve this). It is set using table.setAttribute('style', style) and I can confirm that at the time style is set, it has width:100% if I pass that in the text box. So it must be changed to a fixed pixel width later on?

  • mwoutsmwouts Posts: 19Questions: 3Answers: 0

    Is it possible that calling new DataTable changes the style? When I log the style attribute I find, for instance:
    - (before) margin:auto;table-layout:auto;width:100%;caption-side:bottom
    - (after) margin: auto; table-layout: auto; caption-side: bottom; width: 0px;

    I also tried setting the style after calling new DataTable but then the caption was located between the table header and the rows. And if I set the style twice (before and after new DataTable) then I get a stretched table which is consistent with width:100% but is not what I am looking for...

    I also tried dt.columns.adjust() as mentioned in https://datatables.net/forums/discussion/49564/scrolling-header-alignment but that had no effect, probably because my table is not 'visible' at that time.

    Maybe what I am after is more a behavior like overflow-x:scroll; as mentioned in that other thread, e.g. keep the table centered if there are not too many columns, and scroll only if it does not fit the container. Is that possible using only the standard DataTables arguments? (I have transitioned to the layout argument so I don't see myself adding a dedicated div in the dom argument as in the other thread).

  • kthorngrenkthorngren Posts: 20,796Questions: 26Answers: 4,862

    Looking at this example the table tag has style="width:100%" the width of the table has a specific value. Note that Datatables clones the header, displays the cloned header and hides the original header with height: 0;. It looks like the cloned table has the defined width in px. I didn't realize that happened. Sorry for the misleading suggestion.

    I also tried dt.columns.adjust()

    Use columns.adjust() after the table becomes visible. See this example. I'm not sure how you are making the table visible. Maybe there is an event you can use or possibly a short setTimeout() to execute columns.adjust().

    As a test maybe you can initialize Datatables with the table visible.


  • mwoutsmwouts Posts: 19Questions: 3Answers: 0

    Thanks Kevin! Sorry for taking that much of your time. I have tried columns.adjust() in a simpler context where I think that the table is initially visible, but that did not help.

    What I did precisely what to modify the html template in this way:

    dt = new DataTable(table, dt_args);
    dt.columns().adjust(); // <- new

    then I displayed a table with a style set to margin:auto;width:auto, and scrollX=true:

    What I find strange (but I am afraid I know too little of html) is that the table gets a (fixed) width in pixels while dt-scroll-body and dt-scroll-foot get a width of 100%

  • allanallan Posts: 62,497Questions: 1Answers: 10,264 Site admin

    It isn't clear to me that your columns.adjust() call there would happen after the table is made visible. It looks like it is just running immediately after the table is initialised, which wouldn't make any difference since the table initialisation already does column adjustment.

    As a hacky test, try resizing the window - if the table springs into the right size then you will need to discover whatever event it is, or code action that is making the table visible and trigger that. Perhaps it is in a tab, or there is a fade effect on the whole page or something?


  • mwoutsmwouts Posts: 19Questions: 3Answers: 0

    Thank you Allan. Well even if I use setTimeout and check (visually) that the table is visible before dt.columns.adjust() is called, I still get the misaligned header. I have extracted below a short HTML file that shows the issue. Thank you for your help with this!

    <!DOCTYPE html>
      <table class="display nowrap" style="margin:auto;width:auto" id="itables_823f466d_970d_4553_965b_7f4e22937b34">
      <link href="https://www.unpkg.com/dt_for_itables@2.0.10/dt_bundle.css" rel="stylesheet" />
      <script type="module">
        import { DataTable, jQuery as $ } from 'https://www.unpkg.com/dt_for_itables@2.0.10/dt_bundle.js';
        document.querySelectorAll("#itables_823f466d_970d_4553_965b_7f4e22937b34:not(.dataTable)").forEach(table => {
          // Define the table data
          const data = [["AW", "Latin America & Caribbean ", "<a href=\"https://en.wikipedia.org/wiki/Aruba\">Aruba</a>", "<a href=\"https://en.wikipedia.org/wiki/Oranjestad\">Oranjestad</a>", -70.0167, 12.5167, "<a href=\"https://flagpedia.net/aw\"><img src=\"https://flagpedia.net/data/flags/h80/aw.webp\" alt=\"Flag of Aruba\"></a>"]];
          // Define the dt_args
          let dt_args = { "scrollX": true, "layout": { "topStart": null, "topEnd": null, "bottomStart": null, "bottomEnd": null } };
          dt_args["data"] = data;
          let dt = new DataTable(table, dt_args);
          setTimeout(() => {
            console.log("Adjusting the columns");
          }, 300);
  • allanallan Posts: 62,497Questions: 1Answers: 10,264 Site admin

    Thanks for the extra code!

    Here is that example running: https://live.datatables.net/zixucoyu/1/edit .

    And with width:auto changed to be width:100% it is fixed: https://live.datatables.net/zixucoyu/2/edit

    You can remove the setTimeout with that. The key is to set width:100%.


  • mwoutsmwouts Posts: 19Questions: 3Answers: 0

    Thanks Allan! I agree than setting width:100% aligns the header and table body (and I don't think I need dt.columns.adjust() in that case).

    Let me try to describe what I am trying to achieve:
    1. I am looking for a default that would trigger an horizontal scroll bar for wide tables, but would work also for non-wide tables (and I don't know whether the table is wide or not, so I can't set scrollX dynamically)
    2. I'd like to not stretch the table
    3. I'd like to keep the table centered

    If I use
    - scrollX=False, margin:auto;width:auto (the current default in Jupyter for ITables) then I get 2 and 3 (and Jupyter does provide an horizontal scroll so I almost get 1 - but I don't get that in the newer rendering context called "Streamlit")
    - scrollX=True, margin:auto;width:100% then I get 1 and 3, but a stretched table like here: https://live.datatables.net/zixucoyu/4/edit
    - scrollX=True, margin:auto;width:auto;float:left then I get 1 and 2, but a left sided table like here: https://live.datatables.net/zixucoyu/6/edit
    - scrollX=True, margin:auto;width:auto then I get 1, 2 and 3 - except for the misaligned header :smile:

    Do you see any other way to get 1+2+3 other than aligning the header with the table in the last configuration?

  • allanallan Posts: 62,497Questions: 1Answers: 10,264 Site admin
    Answer ✓

    Thanks! I've got a bitter idea of what you want now, and given that you only want horizontal scrolling, not vertical, what I would do is set the table width to be auto as you originally did (and add margin: 0), but don't enable scrollX. Instead, simply place the table in a div which has overflow: auto.


    That way the table will be as narrow as it can be, but it has space that it can grow into and will overflow horizontally if needed.

    The only downside is that the paging controls / filter would scroll out of sight at well, but that might not be an issue if you aren't using them (like in this example), or you could reposition them.

    If this is what you are looking for, a perhaps better way of doing it, is to make the div that DataTables puts immediately around the table the scrolling element. That can be done with a little CSS.


  • mwoutsmwouts Posts: 19Questions: 3Answers: 0
    edited June 12

    Thank you Allan (and Kevin) for your patience! Yes you're completely correct, the little CSS in your last link is what I was looking for!

    I will now use, in the next PR,

    .dt-layout-table > div {
      overflow: auto;
      max-width: 100%;

    rather than the scrollX option (except possibly in certain cases like the fixed columns...). This seems to work well in Jupyter, in Jupyter Book (= the documentation site) and in Streamlit too. Thank you again!

  • allanallan Posts: 62,497Questions: 1Answers: 10,264 Site admin

    Yeah, that workaround won't work with FixedColumns (or probably FixedHeader) unfortunately.

    I had actually looked at using this method in DataTables 2 when vertical scrolling wasn't enabled. It solves a lot of issues, but introduces others...! You win some, you loose some. This one is a score draw :)


Sign In or Register to comment.