home / tils / til

Menu
  • GraphQL API

til: django_django-admin-horizontal-scroll.md

This data as json

path topic title url body html shot created created_utc updated updated_utc shot_hash slug
django_django-admin-horizontal-scroll.md django Usable horizontal scrollbars in the Django admin for mouse users https://github.com/simonw/til/blob/main/django/django-admin-horizontal-scroll.md I got a complaint from a Windows-with-mouse user of a Django admin project I'm working on: they couldn't see the right hand columns in a table without scrolling horizontally, but since the horizontal scrollbar was only available at the bottom of the page they had to scroll all the way to the bottom first in order to scroll sideways. As a trackpad user I'm not affected by this, since I can two-finger scroll sideways anywhere on the table. (I've had the same exact complaint about Datasette in the past, so I'm very interested in solutions). Matthew Somerville [on Twitter](https://twitter.com/dracos/status/1384391599476641793) suggested setting the maxmimum height of the table to the height of the window, which would cause the horizontal scrollbar to always be available. Here's the recipe I came up with for doing that for tables in the Django admin: ```html <script> function resizeTable() { /* So Windows mouse users can see the horizontal scrollbar https://github.com/CAVaccineInventory/vial/issues/363 */ if (window.matchMedia('screen and (min-width: 800px)').matches) { let container = document.querySelector("#changelist-form .results"); let paginator = document.querySelector("p.paginator"); if (!container || !paginator) { return; } let height = window.innerHeight - container.getBoundingClientRect().top - paginator.getBoundingClientRect().height - 10; container.style.overflowY = "auto"; container.style.height = height + "px"; } } window.addEventListener("load", resizeTable); </script> ``` I added the `window.matchMedia()` check when I realized that this approach wasn't useful at mobile screen sizes. Here `#changelist-form .results` is a `<div>` that wraps the main table on the page, and `p.paginator` is the pagination links shown directly below the table. I decided to set the vertically scrollable height to `window height - top-of-table - paginator height - 10px`. I added this code to my project's custom `admin/base_site.html` template, which now looks something like this: ```html+django {% extends "admin/base_site.html" %} {% block footer %} <div id="footer"></div> <script> /* Script goes here */ </script> {% endblock %} ``` The end result looks like this: <img width="1355" alt="Select_county_to_change___VIAL_admin" src="https://user-images.githubusercontent.com/9599/115450508-d4c6cd00-a1d0-11eb-8efd-5561a630337c.png"> <p>I got a complaint from a Windows-with-mouse user of a Django admin project I'm working on: they couldn't see the right hand columns in a table without scrolling horizontally, but since the horizontal scrollbar was only available at the bottom of the page they had to scroll all the way to the bottom first in order to scroll sideways.</p> <p>As a trackpad user I'm not affected by this, since I can two-finger scroll sideways anywhere on the table.</p> <p>(I've had the same exact complaint about Datasette in the past, so I'm very interested in solutions).</p> <p>Matthew Somerville <a href="https://twitter.com/dracos/status/1384391599476641793" rel="nofollow">on Twitter</a> suggested setting the maxmimum height of the table to the height of the window, which would cause the horizontal scrollbar to always be available.</p> <p>Here's the recipe I came up with for doing that for tables in the Django admin:</p> <div class="highlight highlight-text-html-basic"><pre><span class="pl-kos">&lt;</span><span class="pl-ent">script</span><span class="pl-kos">&gt;</span> <span class="pl-k">function</span> <span class="pl-en">resizeTable</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-c">/* So Windows mouse users can see the horizontal scrollbar</span> <span class="pl-c"> https://github.com/CAVaccineInventory/vial/issues/363 */</span> <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-smi">window</span><span class="pl-kos">.</span><span class="pl-en">matchMedia</span><span class="pl-kos">(</span><span class="pl-s">'screen and (min-width: 800px)'</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-c1">matches</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">let</span> <span class="pl-s1">container</span> <span class="pl-c1">=</span> <span class="pl-smi">document</span><span class="pl-kos">.</span><span class="pl-en">querySelector</span><span class="pl-kos">(</span><span class="pl-s">"#changelist-form .results"</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-k">let</span> <span class="pl-s1">paginator</span> <span class="pl-c1">=</span> <span class="pl-smi">document</span><span class="pl-kos">.</span><span class="pl-en">querySelector</span><span class="pl-kos">(</span><span class="pl-s">"p.paginator"</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-c1">!</span><span class="pl-s1">container</span> <span class="pl-c1">||</span> <span class="pl-c1">!</span><span class="pl-s1">paginator</span><span class="pl-kos">)</span> <span class="pl-kos">{</span> <span class="pl-k">return</span><span class="pl-kos">;</span> <span class="pl-kos">}</span> <span class="pl-k">let</span> <span class="pl-s1">height</span> <span class="pl-c1">=</span> <span class="pl-smi">window</span><span class="pl-kos">.</span><span class="pl-c1">innerHeight</span> <span class="pl-c1">-</span> <span class="pl-s1">container</span><span class="pl-kos">.</span><span class="pl-en">getBoundingClientRect</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-c1">top</span> <span class="pl-c1">-</span> <span class="pl-s1">paginator</span><span class="pl-kos">.</span><span class="pl-en">getBoundingClientRect</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-c1">height</span> <span class="pl-c1">-</span> <span class="pl-c1">10</span><span class="pl-kos">;</span> <span class="pl-s1">container</span><span class="pl-kos">.</span><span class="pl-c1">style</span><span class="pl-kos">.</span><span class="pl-c1">overflowY</span> <span class="pl-c1">=</span> <span class="pl-s">"auto"</span><span class="pl-kos">;</span> <span class="pl-s1">container</span><span class="pl-kos">.</span><span class="pl-c1">style</span><span class="pl-kos">.</span><span class="pl-c1">height</span> <span class="pl-c1">=</span> <span class="pl-s1">height</span> <span class="pl-c1">+</span> <span class="pl-s">"px"</span><span class="pl-kos">;</span> <span class="pl-kos">}</span> <span class="pl-kos">}</span> <span class="pl-smi">window</span><span class="pl-kos">.</span><span class="pl-en">addEventListener</span><span class="pl-kos">(</span><span class="pl-s">"load"</span><span class="pl-kos">,</span> <span class="pl-s1">resizeTable</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-kos">&lt;/</span><span class="pl-ent">script</span><span class="pl-kos">&gt;</span></pre></div> <p>I added the <code>window.matchMedia()</code> check when I realized that this approach wasn't useful at mobile screen sizes.</p> <p>Here <code>#changelist-form .results</code> is a <code>&lt;div&gt;</code> that wraps the main table on the page, and <code>p.paginator</code> is the pagination links shown directly below the table. I decided to set the vertically scrollable height to <code>window height - top-of-table - paginator height - 10px</code>.</p> <p>I added this code to my project's custom <code>admin/base_site.html</code> template, which now looks something like this:</p> <div class="highlight highlight-text-html-django"><pre><span class="pl-e">{%</span> <span class="pl-k">extends</span> <span class="pl-s">"admin/base_site.html"</span> <span class="pl-e">%}</span> <span class="pl-e">{%</span> <span class="pl-k">block</span> <span class="pl-s">footer</span> <span class="pl-e">%}</span> &lt;<span class="pl-ent">div</span> <span class="pl-e">id</span>=<span class="pl-s"><span class="pl-pds">"</span>footer<span class="pl-pds">"</span></span>&gt;&lt;/<span class="pl-ent">div</span>&gt; &lt;<span class="pl-ent">script</span>&gt;<span class="pl-s1"></span> <span class="pl-s1"><span class="pl-c"><span class="pl-c">/*</span> Script goes here <span class="pl-c">*/</span></span></span> <span class="pl-s1"></span>&lt;/<span class="pl-ent">script</span>&gt; <span class="pl-e">{%</span> <span class="pl-k">endblock</span> <span class="pl-e">%}</span></pre></div> <p>The end result looks like this:</p> <p><a href="https://user-images.githubusercontent.com/9599/115450508-d4c6cd00-a1d0-11eb-8efd-5561a630337c.png" target="_blank" rel="nofollow"><img width="1355" alt="Select_county_to_change___VIAL_admin" src="https://user-images.githubusercontent.com/9599/115450508-d4c6cd00-a1d0-11eb-8efd-5561a630337c.png" style="max-width:100%;"></a></p> <Binary: 82,230 bytes> 2021-04-20T12:06:55-07:00 2021-04-20T19:06:55+00:00 2021-04-21T09:48:50-07:00 2021-04-21T16:48:50+00:00 a0304657cacaf66cbb241ccaf0671d50 django-admin-horizontal-scroll
Powered by Datasette · How this site works · Code of conduct