<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bernd Wechner</title>
    <description>The latest articles on Forem by Bernd Wechner (@thumbone).</description>
    <link>https://forem.com/thumbone</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F172568%2F932a43e4-4772-4688-9d5f-b12dd9c4ed56.jpeg</url>
      <title>Forem: Bernd Wechner</title>
      <link>https://forem.com/thumbone</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/thumbone"/>
    <language>en</language>
    <item>
      <title>Hero to Zero: How Not To Manage Staff Redundancies</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Sat, 21 Dec 2024 07:57:11 +0000</pubDate>
      <link>https://forem.com/thumbone/hero-to-zero-how-not-to-manage-staff-redundancies-jl1</link>
      <guid>https://forem.com/thumbone/hero-to-zero-how-not-to-manage-staff-redundancies-jl1</guid>
      <description>&lt;p&gt;Are you suffering a downturn in business? Need to downsize? Well, lend me an ear, and I'll tell you a tale, a tale of what &lt;strong&gt;not&lt;/strong&gt; to do ... with a hint on what you &lt;em&gt;can&lt;/em&gt; do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh02mlblijpjtg3c813x0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh02mlblijpjtg3c813x0.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Redundancies are a fact of life. It's a pretty word used as a euphemism if you will, for sacking or firing, which is, by intent, impersonal. This has nothing to do with the staff member concerned, it's not related to their performance in any way shape or form, but money is short and hard decisions need to be made. Some jobs are declared "redundant". Which may, on occasion, be true, and yet it will also be used, as often as not, to simply describe the fact of necessary cost reduction – for which &lt;em&gt;unaffordable&lt;/em&gt; is a better word than redundant (and arguably more flattering to the staff thus affected).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famb34ekkmsdyzn2xi7r1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famb34ekkmsdyzn2xi7r1.png" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've witnessed quite a few redundancies and been made redundant no less than thrice. The biggest farce I recall was a wave of what we called voluntary redundancies in the 1980s. It swept the steel industry at the time only to find that, well, the people who opted voluntarily to move on (a voluntary redundancy) were those that were confident of finding one jobs, the most employable people. The ones who stayed were those least confident that could find another job, the least employable. It was unilaterally considered a bad move after the fact and after the first wave, it became voluntary redundancy on approval!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq6fcsyv03js96mh7bwsi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq6fcsyv03js96mh7bwsi.png" alt="Image description" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But what I'm here for is to recount the tale of a redundancy that epitomises to my mind what &lt;strong&gt;not&lt;/strong&gt; to do, to your colleagues and fellow human being, an experience so unnecessarily dehumanising and hostile it defies comprehension.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu33obf2t6kyhx7ba4bmq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu33obf2t6kyhx7ba4bmq.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Whence it came
&lt;/h1&gt;

&lt;p&gt;To be clear, I recount the tail of my own redundancy. But to understand it, we need to understand its context, and from whence it stems.&lt;/p&gt;

&lt;p&gt;A small to medium business, that in the 22 years I worked there in its various names and guises, wavered between about 10 and 30 staff members at a guess. I was hired, on the back of a few years working in the field of training course design and delivery and management system installations across a global customer base, to design and deliver a training course for this business in a field of science new to me, that had been sold for delivery in 12 weeks but didn't actually exist yet. Always up for a challenge, I took home a text book from which I read cover to cover (the bible of this science) and for my first day, met my new boss at the airport, for a trip to Malaysia where he was presenting the product and felt this would be as good an intro as any.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk9kh6sjdxc8rolbd4de.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffk9kh6sjdxc8rolbd4de.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Back at home base later, I took on front line customer support for their software product, and writing all the version 3 help file updates as part of the upskilling I had to do, while working on a three-day training course in the use of this software and the basic principles of the science that apply.&lt;/p&gt;

&lt;p&gt;The course was a hit. Delivered in Seattle and Anchorage with warm and positive feedback. And I went on to deliver it in various other places before ... a child was born. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F786dzb138aqvby3lm9u9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F786dzb138aqvby3lm9u9.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At that point, I wanted to reduce my travel (I'd been on the road for most of a decade by then and for two years prior to taking this job, in aeroplanes every weekend to deliver a training course, and install systems across the world and had enough and wanted to be there for my daughter. So I did a job swap in house, with a guy we'd hired into Quality Assurance, and so began my sojourn there.&lt;/p&gt;

&lt;p&gt;In time, I dropped to a 4-day week wanting to be a present father in the life of my daughter, and we grew our business to purchase another, and then start a new blue sky division that would produce a new product into a new market to diversify our interests and protect the business against the vagaries on one product in one market. Along the way, I progressed to managing the Customer Support, Quality Assurance and Documentation needs across three business teams with teams of about a dozen individual support staff, testers and writers.&lt;/p&gt;

&lt;p&gt;To fund this blue-sky product, we courted venture capitalists, and so had a glorious time of growth. Alas, internal conflicts and mismanagement also ensured we were running out of money and time and nowhere near an MVP (Minimum Viable Product) and I moved into project management tasked with delivery of a v1 MVP before we ran dry. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8nyrxowtq7k0ekc30f68.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8nyrxowtq7k0ekc30f68.jpg" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I worked on estimation and scheduling methods and after a false start, we got a v1 MVP out on the committed date with the committed feature set. Followed that with a v1.1 service release 3 months later on time, on budget. &lt;/p&gt;

&lt;p&gt;But sales were slow, too slow, and we ran out of money. The team was folded down to minimal size, a programmer, a tester, a salesman, and I was snapped up into the sales team on our cash-cow product. My first redundancy. No paperwork, nothing formal, all verbal agreements and org chart alterations.&lt;/p&gt;

&lt;p&gt;I started by working no necessary upgrades to an in-house license management system (C#, ASP .NET, and MS-SQL work) and direct sales in the Asia Pacific region. One surprise after another, not long after I was managing global sales and marketing for the product and a team of sales staff and resellers.&lt;/p&gt;

&lt;p&gt;At which point more surprises, we lost our CEO and held internal interviews for someone to fill those shoes. I felt fully capable of the task, but nevertheless compelled to recommend a colleague who I thought would do a better job of it (besides, I wasn't really sure what a CEO does all day, and didn't much savour the idea). So my colleague took the job and I managed Sales under him.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p6ht4fr93fuy99lg42m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1p6ht4fr93fuy99lg42m.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I pushed for significant reforms to sales and marketing (pricing reforms, reseller network growth, marketing and growth of passive sales - we had grown to this point on direct sales, rebranding with new brochures and conference and trade show bling and uniforms for our staff). I secured only some of that before my colleague decided he didn't really know what a CEO did all day either or, better said, and more honestly, decided that it wasn't for him, resigned and became a school teacher (which I dare say did not include a pay rise - which to my mind is a touching measure of what it means to be doing something you love - he's loving teaching to this day, and was a pretty good CEO too I should add).&lt;/p&gt;

&lt;p&gt;A new CEO on deck (again with my recommendation and support) I did ultimately execute all those reforms and then turned to focus on the business relations as the global partner manager, growing our internation reseller network and empowering them to sell more rapidly, and courting the hardware suppliers we sold on the back of for OEM style deals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccu77hklls9hhlbx8o5k.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fccu77hklls9hhlbx8o5k.jpg" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In time though I pushed that as far as it would go readily, and I itched once more for creative work on the development cycle and negotiated with my CEO for an opportunity back in the development team. Out of the blue one-day I was called in to a meeting, and served a redundancy notice, with an offer to take a job in Quality Assurance on the development team. &lt;/p&gt;

&lt;p&gt;My second redundancy with an offer in the development team as I'd sought. No-brainer really. Not my first and top preference, but as it happens Quality Assurance was one area in the team that felt could use some attention, some work on automation and rigour in particular and I had a lot to offer there. I had learned Python (and Matlab) on my tenure in Sales (as our customer base was using Matlab, Python and R and I hadn't got around to R yet) and could see great opportunities to apply that in this area. I fed back all the same to my CEO that I did not like this style one jot. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyg654zp13srhotadrvxg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyg654zp13srhotadrvxg.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would much rather be called into a meeting to discuss the plan of moving to quality assurance, and have the business needs and priorities on the table before the deal is finalised before it is a fait accompli and a formal redundancy with what can only be seen as a rather impersonal and concerning - decision made behind my back. At this point I was reporting directly to the CEO - a position I was almost offered at one point - and had been working closely with him for 5 or more years and had what I felt was a personable and friendly relationship. Such an unexpected, surprise delivery of dry formal decision made behind my back was most unwelcome in that context.&lt;/p&gt;

&lt;p&gt;I did however, appreciate action on the front of my efforts to negotiate a return to the development team and creative expression and the offer of a place in that team and also credited them with solid efforts to keep the remunerative impacts modest and minimal. All the same I had two young sons now and found that on a 30 hour week that I was rushing everywhere all the time so I asked if as part of this transition I could drop to 25 hours a week, 5 x 5 hour workdays. That was granted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl47qctw6px2wyg7ctgrg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl47qctw6px2wyg7ctgrg.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On a personal front, 5 hour days are the thing! With two young sons in early primary school I was able to walk them to school and a good few afternoons collect them and take them to various sporting activities. I could be, what my father never was for me, present, there, in their lives and they in mine. And there is no greater gift.&lt;/p&gt;

&lt;p&gt;I worked five odd years on the quality assurance team, cutting a niche there for the heavy mathematical work (wideband signal analyses among other things - I am still a telecoms engineer deep down I guess) and data science work (file formats and such) and I made huge strides in rigour and automation for the team. &lt;/p&gt;

&lt;p&gt;Not without hiccups. On account of laudable diversity in our team and my re-entry as a peer after a decade of absence and some new staff and some old staff, my efforts to modernise were not immediately met with open arms. We'd adopted Teams over COVID and on a channel for sharing innovations I'd announced an early one I was excited about a massive time saver and rigour-boost in regression test analyses, and was publicly dissed by a colleague. That escalated quickly ... but a disciplinary meeting aside (on the subject of dissing colleagues in public, where a private communication would serve to resolve any misunderstanding or disagreements before going public) my tail between my legs and head tucked in, in-house public announcements were avoided and progress continued admirably in consultation with the QA lead and other development staff as needed. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftb2o0auutl4f0yawjljs.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftb2o0auutl4f0yawjljs.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had just got this to the point of good enough, and a desire to present new tools and methods first to a select group of receptive folk and then with some luck to the whole team. Tools for increasing the rigour and efficiency of our QA efforts and first moves into full automation of our UI testing (big job to do well and would need full team buy-in). &lt;/p&gt;

&lt;p&gt;December was approaching, and our half yearly reviews and I was going  to table the possibility of a roll out and presentation, to sound that out, I felt the time was right, work sufficiently mature and the team culturally ready.&lt;/p&gt;

&lt;p&gt;My goal was to present this and seek an exit from the business at some point. I had done my run on quality assurance here now and was keen for a change, a new adventure. I had been looking on and off for other opportunities for years and my poor wife had always to remind me that I had a job most people dream of. I had 5-hour work days on a good rate, could come and go mostly as I pleased and work fairly autonomously. What's not to love? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2sf2adfqfwz8sys695j.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu2sf2adfqfwz8sys695j.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well for me, there the challenge. I had more to do on this job but it was definitely exiting the innovation, and proof of concept phase, well into evolution and now needed commitment and hard yards and well it's not that I wouldn't be up for that, but it'd been 5 years on this and well, that's been more or less my limit to date. &lt;/p&gt;

&lt;p&gt;My CV is littered with me getting antsy after 3-5 years and looking for something new to do, new stuff to learn and master. New problems to solve. Things fix, build create. And I didn't want another 5 years on this really, it was enough. But she (my wife) had a point, I had two young sons a mortgage, and well, financial security held great appeal as did this wonderful flexibility to be part of my boy's lives. So I never acted on it, just flirted with the idea and dropped the odd EOI (expression of interest - a term I use here for something far more modest than a job application).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fedvir2jglxvlojs0g6oj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fedvir2jglxvlojs0g6oj.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Redundant again
&lt;/h1&gt;

&lt;p&gt;(sung to the tune of &lt;a href="https://www.youtube.com/watch?v=S8KDXntYwGQ" rel="noopener noreferrer"&gt;Sick Again&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Unbeknownst to me, the perfect storm was brewing ... &lt;/p&gt;

&lt;p&gt;It was late spring, the days were long and on Wednesday afternoon I was landscaping the front garden at home and had pulled a back muscle in the process, doh! Woke Thursday stiff and sore. Been here before, and not a fan, and know to be careful (I remmeber the time it took two morphine shots before they could pick me upoff the floor one time I tripped this back of mine, not good, not keen on a repeat). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ihhsrz441ydieo6xd0g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ihhsrz441ydieo6xd0g.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Still, I also know that gentle movement is better than not, so I did ride my bike into the office (late, because I was working the afternoon shift anyhow on account an egagement in town that night) - I commute on a bicycle BTW. I put in a days work (at my standing desk). Leaving the office late-ish, and just putting my helmet on by the front door, still doubled over a little with stiff sore back and wonderig how wise it was to ride in, my CEO pases me on the way to his car and thrusts a letter into my hand (I bet you know where this going ...).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5pz3bq5zxrqcjxu6cb2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5pz3bq5zxrqcjxu6cb2.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I started opening and he said "no need to open it now" and continued to the car. I did open it now. A redundancy notice. Hmmmm. &lt;/p&gt;

&lt;p&gt;Really didn't take my feedback to heart last time it seems. But this time was going to be a new level of twisted ...&lt;/p&gt;

&lt;p&gt;It announced a meeting on Monday Nov 11. A meeting, obligated by law, in which I was to have an opportunity to suggest re-deployment options to consider. The letter was dry and impersonal, pointed out clearly that this was a business decision to do with my role not performance related, or personal in any way. &lt;/p&gt;

&lt;p&gt;
  A redacted copy of the letter I received
  &lt;br&gt;
Redacted so as to protect all third parties from  any implied criticism or otherwise. A mere matter of record so you can see what a formulaic redundancy letter reads like, bearing in mind this comes out of the blue with no forewarning and is thrust into your hand by a colleague (the CEO you've worked closely with for years).&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshrkgc295c5po67ldt90.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshrkgc295c5po67ldt90.png" alt="Image description" width="769" height="1100"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feiw3lwtnvefl2eotn133.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feiw3lwtnvefl2eotn133.png" alt="Image description" width="769" height="1100"&gt;&lt;/a&gt;&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;Friday morning my back was worse so I on the floor with phone in hand. Went to write my team on teams that I'd be taking the day off with abad back and found I was locked out of Teams, and Outlook. Texted my CEO:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I notice that on my phone Teams and Outlook logged out and I can't log on. Related? Or poorly timed coincidence?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;he replied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Yes. Related. It's normal procedure to stop access in the period between initial notice and final determination. You are given the time off. We can discuss further on Monday.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I lost a lot of sleep that weekend. Here I was 22 years in the business a decade working with this CEO, in a busines of about 20 people, who all new each other well, mostly as friends, and socilised together. I had worn almost every hat one can wear in the business. And I'm treated suddenly like a threat. A danger. A risk. &lt;/p&gt;

&lt;p&gt;Normal procedure? For ASIO? the CIA? a Military base? An Embassy? But us? Seriously? Or is this a case of tin-pot depotism gone wild, the little master of little kingdom? THis is not "normal" anywhere that I have worked ever and I've worked in some big places. I've worked in a national telcom, in a national steel producer, and nowhere have I ever been where my boss would not have bent the normal bureacratic procedure (which is far more rigid in national enterprises than a 20 strong family of IT workers) as far as they could to make the experience friendly and human. Not a single one, ever.&lt;/p&gt;

&lt;p&gt;Heck, I kid ye not when I say, I would afford my enemies that luxury if I were in that position (in fact I have let folk go and I have been in that position and I know how to do it - oh and I do have enemies - in the words of Victor Hugo "You have enemies? Why, it is the story of every man who has done a great deed or created a new idea."). To wit, the sleep I lost that weekend was not to sorrow, nor pity, nor even anger, but to WTF (is there an emotion for that? ChatGTP thinks dumbfounded astonishment, baffled exasperation or stunned confusion, so no, no simple emotion and I think WTF has brevity benefits).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe427ardhixf13a7he52c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe427ardhixf13a7he52c.jpg" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I get an invite to Monday's meeting on Teams. Ironic, given I have no access to Teams. Described with "This meeting scheduled as per my letter. Please let me know if you’d prefer in-person.". I replied "I'd rather come in thanks." and did. Whatever goes down here can be done looking me in the eye, not over a camera or worse a voice link. Just how many steps can we take to dehumanise this I wondered?&lt;/p&gt;

&lt;p&gt;As per the letter this meeting was for them to provide the full context of the redundancy and to afford me the opportunity to propose a redeployment. &lt;/p&gt;

&lt;p&gt;The reason was amusing. And no surprise. I might even have predicted it. But still the tight adherence to minimal legal obligations here and the language twists that demands amuse me, I admit. &lt;/p&gt;

&lt;p&gt;To put that into context there is no way that a QA role is redundant in this business. There are 4 other staff employed on QA. As it happens though because that blue-sky product failed and our venture capitalists basically lost their money, they wanted to grab the money and run - time to sell the business while the cash cow was earning. &lt;/p&gt;

&lt;p&gt;We started this process at the time that I moved into Sales and in fact I was on the team driving it and gave the project its name, Bacchus (a pun on "back us" and the Roman god of revelry and wine). But in all the years since, we had come close once or twice but never clinched a sale. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fitk2is90ogbskge2py1b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fitk2is90ogbskge2py1b.jpg" alt="Image description" width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I managed sales we'd been on a rising sales trajectory but it was levelling out (also predicted) but because expenditures tracked the very questionable year on year 20% growth targets in what is a very niche market sector, 2023 was apparently the first year we ran at a loss, cash negative. We could afford it, had cash in the bank, but nevertheless we ran at a loss. We did have a record quarter following that too, but then these are the vagaries of a small-niche global market (we're quite sensitive to exhange rates too trading globally often in local currencies). &lt;/p&gt;

&lt;p&gt;Anyhow, a sale was looking good when we were growing and after last year we need a) to cut costs and b) to look at how we can be more "saleable". And in the back room, the decision was made that a part time QA role did not align with that need. Another part-timer was made renundant too (in the admin area). So the irony is, I'm redundant because of project Bacchus that I was part of from the outset (albeit no more, since I moved out of upper management into QA I was no longer involved).&lt;/p&gt;

&lt;p&gt;The opportunity to propose redeployment made me laugh. And I shared as much in the meeting. I mean they know, and I know, that I know how the business runs. I know the nature of the business. And I know as well as they know that I'm not genuinely redundant, I'm just not afforable (they can probably hire a graduate full time into a full time QA role for what they pay me). &lt;/p&gt;

&lt;p&gt;And they know too, that without the books on the table, recent years financial reports, and the coming years credible projections (with a view on the sales pipeline - our cash cow has a 6-18 month sales pipeline is a budgeted-for product not a discretionary expense) and above all, minutes from recent board meetings. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xmsfdzaorc49ez78d0g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xmsfdzaorc49ez78d0g.jpg" alt="Image description" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of which I'd want, nay need, to make a credible and serious propsal for a redeployment. I reckon I'd find one, no guarantee they'd take it but to pitch one I'd need to do the business analysis which after 5 years in QA I was a little blind to (mushrooms as they say, kept in the dark and fed ...). So no, I had no capability to pitch a credible redeployment opportunity, thanks for invite though.&lt;/p&gt;

&lt;p&gt;The redundancy includes a mandated 5 weeks of notice. But they are happy to pay that out, I don't have to work it if I don't want to. But if I want to have some time for a handover it's available. Again, I look at these guys (two of them in the meeting I waived my right and invite for a support person) with a cocked eyebrow. You're saying you'll pay me regardless and I can come in and work here or spend my time looking for another job? Um is that a real question? I told them I could use a day maybe two to document all I'd done and have some hand over meetings and/or presentations and I would like to do that. &lt;/p&gt;

&lt;p&gt;I told them again, that it would have been trivial to have a meeting before this letter, as colleagues and friends. We could have come to an agreement we could have timed it better, I'm fine with moving on, I have no need for redeployment, but here we are on my birthday discussing my dismissal, a mere week after a positive RSVP to the staff Christmas party. Merry Christmas Scrooge. &lt;/p&gt;

&lt;p&gt;They recoiled a little "It's you're Birthday!?". "Nah, it's tomorrow". They relax visible. I retort "That's all right then I guess". I let them know, here I am 22 years in, much of it in senior management, a sharehlder of the business, and you shove a letter into my hand and lock me out of all the systems, like I'm a spy, thief or vandal or emotionally unstable. "It's not personal, just the 'process'".&lt;/p&gt;

&lt;p&gt;It's not personal? Catch-22 my friend. The fact that in a workplace of under two dozen colleagues, with a guy you've worked with for a decade, who recommended you to the CEO position, who's been here 22 years and calls this place family, your precious "process" can't  flex a little to make it personal. &lt;strong&gt;That&lt;/strong&gt; is personal. That you couldn't be bothered making this process more personal for me.In refusing to make it personaly, you have made it personal. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycf92oyn8x4sq3v4zwok.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fycf92oyn8x4sq3v4zwok.jpg" alt="Image description" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The CEO made it clear that he takes his legal obligations very seriously. I thanked him for that. It is appreciated. All the same it seems minimal observance and not one jot more is what he's keen on here.&lt;/p&gt;

&lt;p&gt;
  A redacted copy of the letter I received to summarise the followup meeting
  &lt;br&gt;
Redacted so as to protect all third parties from  any implied criticism or otherwise. A mere matter of record so you can see how the story closed from the business side. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ewx1in85hg4xbtzo0xj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ewx1in85hg4xbtzo0xj.png" alt="Image description" width="769" height="1100"&gt;&lt;/a&gt;&lt;br&gt;


&lt;/p&gt;

&lt;p&gt;I had to take a week off for this bad back anyhow, but then come in and clear my desk. I asked if I could come in on the Monday after and my CEO wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Under the circumstances, and given your views about the redundancy process and our workplace (dehumanising, hostile, treated like a statistic), I do not believe that it’s appropriate for you to attend the premises during normal work hours. 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agan, WTF? He read the feedback on his "process" right, but it has actually nothing to do with the workplace or anyone in it, including him. Let's be clear, this is a lovely man, and a great colleague, a wonderful salesman and CEO. We simply differ on the relative priorties of "process" and "humanity" in a small business where everyone knows one another, we're all family. But shrug. Evidence in hand, dehumanising and hostile. So I had to come in out of hours. &lt;/p&gt;

&lt;p&gt;I also asked about coming in for a day to document my assets and present some materials as a handover - given I'm technically employed for 5 weeks (the notice period). His reply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;To re-confirm, your last day of employment here was Monday 11 November and you have been fully paid in lieu of notice period. You are not “technically employed” ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fda7xhuvg7gie863jjwsm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fda7xhuvg7gie863jjwsm.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  How to execute a redundancy
&lt;/h1&gt;

&lt;p&gt;As per my CEO's express intent, abide by the law. But the law, as a rule is there to protect employees from abuse (in Australia it is and we're governed under the Professional Employees Award 2020 for example), and &lt;strong&gt;don't&lt;/strong&gt; use it as a guide to limit what you do to make this process human. The law tables the minimal requirements it is not a guidebook.&lt;/p&gt;

&lt;p&gt;Remember you are dealing with people. And you are not a monster. Don't let anyone convince you of it and don't let them tell you the "process" must be followed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 1:&lt;/strong&gt; Meet first. Never deliver a letter first. Talk first. Let them know what's up in person. Not like some legal notice. We're not in court. We work together. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi0oaqsp0p487lh5hu6b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi0oaqsp0p487lh5hu6b.jpg" alt="Image description" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rule 2: Because you followed Rule 1 and met first, never treat them like a threat, or a danger, like someone who's going to steal from you or vandalise you, unless you assess a genuine risk of just that. Which is one more reason to follow Rule 1, to assess the risk of and emotionally unstable repsonse. Never suppose someone is a risk, never mind someone you know very well isn't, someone who is a member of the family, a colleague, friend, shareholder, invested in the business.    &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbdz8pcpu9p4829vabbk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbdz8pcpu9p4829vabbk.jpg" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Really it's not that hard, two basic rules to keep the process human.&lt;/p&gt;

&lt;p&gt;Yes, if you're kicking someone out of the CIA maybe make it clean and fast. But if you're a private business no matter how large, try, for the love of all you respect, to keep it human, two simple rules (consult them first, and don't treat them like a threat unless they are) to keep it human. In a huge business you may need to stand for that. As the CEO you have zero excuse and only a lack of caring and desire or, of course, that saving dodge: "this hurts me more than it hurts you, but unfortunately my hands are tied, this is the 'process' and I wish it were better" ... doesn't (dodge the responsibility that is). You take the wrap. &lt;/p&gt;

&lt;h1&gt;
  
  
  Disclaimer
&lt;/h1&gt;

&lt;p&gt;I have opted to use a very personal experience to illustrate two simple rules to follow that can make redundancy a human experience and not a hostile one. In so doing it needs to be stated there are no ill feelings and now judgements surrounding the matter. In sharing a personal story there is the risk of inadvertently embarrassing someone and to avoid that I have carefully removed all identifying materials and references as best I can. All the people in this story are wonderful people, doing the best vthey can with the skills, knowledge, tools and pressures they have and face. The story attempts to capture the sense of surprise and wow, surrounding the dehumanisation of a close colleague and attempts only to pinpoint the key difference in style or opinion that exists between key characters in that story. Both are presented:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The view that "process" exists to be followed, and that laws should be adhered to minimally. &lt;/li&gt;
&lt;li&gt;The view that "people" matter more than process and that laws do no limit how nice and human you can be, they only set minimal standards.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Availability
&lt;/h1&gt;

&lt;p&gt;Well, given the nature of the story told, I am of course as at December 2024 available for exciting and interesting work opportunities again. Though I am enjoying a holiday too ;-).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bernd.wechner.info/Professional/" rel="noopener noreferrer"&gt;A Professional Résumé&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/bernd-wechner-7601bb/" rel="noopener noreferrer"&gt;LinkedIn Profile&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.seek.com.au/profile/bernd-wechner-MSlcwNZM1M" rel="noopener noreferrer"&gt;Seek Profile&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Attribution
&lt;/h1&gt;

&lt;p&gt;This a people story, and not a particularly short one, so I wanted some nice people pictures to break up the prose a bit. I found those on &lt;a href="https://www.freepik.com/" rel="noopener noreferrer"&gt;Freepik&lt;/a&gt; which has a good selection of free images that can be used with attribution (a limit of 3 downloads a day anonymously, and 10 per day for an account). &lt;/p&gt;

</description>
      <category>management</category>
      <category>leadership</category>
      <category>career</category>
      <category>redundancy</category>
    </item>
    <item>
      <title>Dumping Data with Python's CSV DictWriter</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Tue, 30 May 2023 08:34:33 +0000</pubDate>
      <link>https://forem.com/thumbone/dumping-data-with-pythons-csv-dictwriter-1g0</link>
      <guid>https://forem.com/thumbone/dumping-data-with-pythons-csv-dictwriter-1g0</guid>
      <description>&lt;p&gt;I really love Python's &lt;a href="https://docs.python.org/3/library/csv.html" rel="noopener noreferrer"&gt;csv&lt;/a&gt; module. But I do wish it was a little better documented. &lt;/p&gt;

&lt;p&gt;The DictWriter lets you write CSV files very neatly and semantically by defining each row as a Python &lt;a href="https://docs.python.org/3/library/stdtypes.html#dict" rel="noopener noreferrer"&gt;dict&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's the example right out of the docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;names.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fieldnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeheader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Baked&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Beans&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Lovely&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Spam&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Wonderful&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Spam&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alas, it overlooks what I would consider a (if not &lt;em&gt;the&lt;/em&gt;) standard use case, the one I come across all the time, in which I don't write the rows out one by one literally, but in a loop, more like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Baked&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Beans&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Lovely&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Spam&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Wonderful&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Spam&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;names.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fieldnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeheader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;datum&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
                         &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The source won't generally be a list of tuples, rather an iterator of objects more generally, and I'm dumping some of their properties.&lt;/li&gt;
&lt;li&gt;I will be working with a &lt;strong&gt;much&lt;/strong&gt; longer list of fieldnames.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;which, raises the spectre of repeating the same list of field names, most definitely not &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;DRY&lt;/a&gt;, and highly undesirable and difficult to maintain as I tweak the list of fields I want to dump to CSV.&lt;/p&gt;

&lt;p&gt;And yet I find no documentation that gets around that. So, having just nutted one out and tested it, it's worth putting down in a document (right here and now).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The problem in a nutshell is that &lt;code&gt;csv.DictWriter&lt;/code&gt; demands to know the &lt;code&gt;fieldnames&lt;/code&gt; (and &lt;code&gt;writer.writeheader()&lt;/code&gt; needs to have them known), but they are specified in the dictionary build inside the loop. &lt;code&gt;fieldnames&lt;/code&gt; is not even an optional argument to &lt;code&gt;csv.DictWriter&lt;/code&gt; and the &lt;code&gt;writer&lt;/code&gt; is &lt;a href="https://docs.python.org/3/library/csv.html#writer-objects" rel="noopener noreferrer"&gt;poorly documented&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;The solution rests in two empirically determined (for lack of documentation) facts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;csv.DictWriter&lt;/code&gt; accepts &lt;code&gt;fieldnames=None&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;writer&lt;/code&gt; if returns has a &lt;code&gt;fieldnames&lt;/code&gt; attribute that can be set post creation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To wit, this works beautifully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Baked&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Beans&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Lovely&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Spam&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Wonderful&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Spam&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;names.csv&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csvfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;datum&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;first_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; 
               &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;last_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datum&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
             &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fieldnames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
             &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeheader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now the row keys are not doubly specified. And the CSV file receives its header row.&lt;/p&gt;

&lt;p&gt;A simple paradigm once discovered, I am using for rapidly dumping CSV files describing objects, mostly for testing and study purposes. It means I can play with the &lt;code&gt;row&lt;/code&gt; definition in situ, adding fields and changing fields etc, without having to change them in two places and I get the benefit of a DictWriter and its simple syntax for writing CSV files.&lt;/p&gt;

</description>
      <category>python</category>
      <category>csv</category>
    </item>
    <item>
      <title>Selecting Things in All of the Nominated Categories</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Thu, 11 May 2023 08:44:00 +0000</pubDate>
      <link>https://forem.com/thumbone/selecting-things-in-all-of-the-nominated-categories-3301</link>
      <guid>https://forem.com/thumbone/selecting-things-in-all-of-the-nominated-categories-3301</guid>
      <description>&lt;p&gt;I came across a problem this week, that I was sure, had a canonical solution (i.e. standard and documented). But no, not that I could find. So yet again a process of reading, searching, testing, trying, talking (on &lt;a href="https://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt;, with &lt;a href="https://chat.openai.com/"&gt;ChatGPT&lt;/a&gt;) and resulting notes and even a &lt;a href="https://codesandbox.io"&gt;sandbox&lt;/a&gt; (demo). &lt;/p&gt;

&lt;h2&gt;
  
  
  Problem Statement
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The context:&lt;/strong&gt; A database (nominally behind a website) containing Things, and those Things are in Categories. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The environment:&lt;/strong&gt; &lt;a href="https://www.djangoproject.com/"&gt;Django&lt;/a&gt; as a web framework and Python as a backend, but raw SQL will do if needed.&lt;/p&gt;

&lt;p&gt;In the Django context the simplest scenario is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name of the Thing&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Name of the Category&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ManyToManyField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Things&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="n"&gt;related_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;categories&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which of course is backed by three tables in a (nominally, &lt;a href="https://www.postgresql.org/"&gt;Postgresql&lt;/a&gt;, though I did trials in &lt;a href="https://www.sqlite.org"&gt;SQLite&lt;/a&gt;) database (in pro forma - Django will actually use slightly different names depending on context for such tables):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;thing&lt;/code&gt; which has one thing per row&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;category&lt;/code&gt; - which has one category per row&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;thing_category&lt;/code&gt; - which has a (thing id, category id) row for each relationship between things and categories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You might prefer to think of things and categories in some other way more familiar to you (anything at all, with a many-to-many relationship suffices), perhaps students and courses, papers and authors, people and interests, food and its properties ... whatever really, I have several applications so asked this question in the general terms of things and categories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The task:&lt;/strong&gt; Given a list of categories, say [1, 4, 7, 23], write one query which returns all the things that are in all of these categories (but not limited to them, i.e. can be in other categories as well).&lt;/p&gt;

&lt;p&gt;In turns out that the partner problem, finding all the things that are in any of those categories is easy. In Django:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categories__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  although...
  &lt;p&gt;this returns duplicate things, a wonderfully bizarre feature of Django, and to prevent that one needs to add &lt;code&gt;.distinct()&lt;/code&gt; &lt;/p&gt;

&lt;/p&gt;

&lt;p&gt;This, in effect, is an ANY/OR result, returning all things that are in any of the listed categories, alternately, all things that are in category 1 &lt;strong&gt;or&lt;/strong&gt; category 4 &lt;strong&gt;or&lt;/strong&gt; category 7 &lt;strong&gt;or&lt;/strong&gt; category 23.&lt;/p&gt;

&lt;p&gt;What I need is an ALL/AND result, returning all things that in &lt;em&gt;all&lt;/em&gt; of the listed categories, alternately, all things that are category 1 &lt;strong&gt;and&lt;/strong&gt; category 4 &lt;strong&gt;and&lt;/strong&gt; category 7 &lt;strong&gt;and&lt;/strong&gt; category 23.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solutions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; 
  solution
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;cat_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categories__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cats&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cat_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cats&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;h3&gt;
  
  
  Field Lookups
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuh5t10v1o27ocexk4gjp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuh5t10v1o27ocexk4gjp.jpg" alt="Field Look Up" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;code&gt;categories__in&lt;/code&gt; is what Django calls a &lt;a href="https://docs.djangoproject.com/en/4.2/topics/db/queries/#field-lookups"&gt;field lookup&lt;/a&gt;. They are very handy and the first port-of-call was to try and find a field lookup that does this. Alas none was to be found. The &lt;code&gt;__in&lt;/code&gt; lookup is used to perform an ANY/OR match. There is no equivalent for ALL/AND lookups like say &lt;code&gt;__in_all&lt;/code&gt; - alas. It's possible to write &lt;a href="https://docs.djangoproject.com/en/4.2/howto/custom-lookups/"&gt;custom lookups&lt;/a&gt; and I have done that before, and was tempted here, but the SQL solution is non-trivial to implement in a custom lookup (can be done, but the method is poorly documented and would need more research).&lt;/p&gt;

&lt;p&gt;Turns out the SQL should I (or someone else) choose to build a lookup like &lt;code&gt;__in_all&lt;/code&gt; can work on the joining table alone, so with the tables and example above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;thing_id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;thing_category&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;category_id&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;thing_id&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works well because the WHERE clause excludes all other categories, so only those in the list will be counted by the Count(*) function. To wit, this fetches all the relations that are (at least) in the listed categories (they may be in more). Precisely what I am targeting. Of course, this assumes there are no duplicate rows in the table (a safe assumption with Django joining tables).&lt;/p&gt;

&lt;p&gt;Interestingly, the &lt;code&gt;__in&lt;/code&gt; lookup which returns the ANY/OR set of things can be written in similar SQL as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;thing_id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;thing_category&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;category_id&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;thing_id&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In lieu of a custom lookup or &lt;a href="https://docs.djangoproject.com/en/4.2/topics/db/sql/"&gt;raw SQL&lt;/a&gt; query though, a better Django solution is preferred.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filtered Counts
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9zb6zaylod6wfxa7ptx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9zb6zaylod6wfxa7ptx.png" alt="Filters" width="800" height="465"&gt;&lt;/a&gt;&lt;br&gt;
Django filters are often a challenge to write. It is both a wonderful abstraction of the underlying database and, at the same time, a very different approach and way of thinking. There's always this small challenge with Django, if you have SQL you know works, translating that to Django filter.&lt;/p&gt;

&lt;p&gt;It turns out the Django &lt;a href="https://docs.djangoproject.com/en/4.2/ref/models/querysets/#id9"&gt;Count&lt;/a&gt; aggregate function has a (poorly documented) filter argument.&lt;/p&gt;

&lt;p&gt;This:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cat_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;categories&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is fairly standard Django (that many will recognise), which adds a count of the categories as an attribute named &lt;code&gt;cat_count&lt;/code&gt; to each of returned &lt;code&gt;things&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But we don't want a count of the categories every thing is in, we want a count of the categories from our list that each thing is in. It turns out Django supports that through the rather poorly documented filter addition to the Count aggregate.&lt;/p&gt;

&lt;p&gt;So we can limit the count to that list of desire categories using a Count filter, and that does use a field lookup! As in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cat_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;categories&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categories__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cats&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we have all things annotated with a count of categories from 0 to 4 (the length of cats) as the Count was only on a filtered set of them. &lt;/p&gt;

&lt;p&gt;To get all things that are in ALL categories we can filter for those that have 4 reported, so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cat_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;categories&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categories__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cats&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cat_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cats&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Revisiting ANY/OR (a digression)
&lt;/h3&gt;

&lt;p&gt;Ironically, we of course replicate what the &lt;code&gt;__in&lt;/code&gt; lookup does using this annotation too, so that this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categories__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is &lt;em&gt;almost&lt;/em&gt; the same (as in, produces the same results as):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;annotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cat_count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;categories&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categories__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cats&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cat_count__gte&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which uses the &lt;code&gt;__gte&lt;/code&gt; field lookup. Alas, the first cut, duplicates things, Django does a lazy join and then filter on the join table, so things appear once per matching category. Doh! Like that would be of use to anyone, ever, in any context? So We have to help Django do the sensible thing and explicitly request distinct things:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;things&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;categories__in&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cats&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now the two queries return near identical results (the ordering can be different unless we specify it). &lt;/p&gt;

&lt;h3&gt;
  
  
  Drilling down to SQL
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwt8kuav210lygb0juu51.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwt8kuav210lygb0juu51.jpg" alt="Drill" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
We can of course ask Django for the SQL it generates (turns out that's not trivial but that is a digression for another article). And it's interesting to compare the SQL that the &lt;code&gt;__in&lt;/code&gt; lookup generates compared with our annotating approach.&lt;/p&gt;

&lt;p&gt;Here's what an __in lookup generates (in pro forma):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;
&lt;span class="k"&gt;INNER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;category_things&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thing_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here's what our annotation approach produces (in pro forma):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;FILTER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
      &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nv"&gt;"cat_count"&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; 
  &lt;span class="n"&gt;category_things&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thing_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; 
  &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; 
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="n"&gt;FILTER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; 
  &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Django's SQL clearly does not adhere to &lt;a href="https://docs.djangoproject.com/en/4.2/misc/design-philosophies/#don-t-repeat-yourself-dry"&gt;DRY&lt;/a&gt;, given it reproduces &lt;code&gt;COUNT(category_things.category_id) FILTER (WHERE category_things.category_id IN (1, 4, 7, 23))&lt;/code&gt; in two places while this is also valid (and tested, it works):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;FILTER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
      &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nv"&gt;"cat_count"&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; 
  &lt;span class="n"&gt;category_things&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thing_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; 
  &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; 
  &lt;span class="n"&gt;cat_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  caveat
  &lt;em&gt;this may be an artefact of the SQL drill down, or generation, which as I said is not trivial in Django, but the subject for another article, point here being is we are not necessarily seeing here the SQL Django actually sends to the database engine - though in all honesty it pretty likely is&lt;/em&gt; 

&lt;/p&gt;

&lt;p&gt;For completeness of course the SQL for the ALL/AND request that I was pursuing at outset becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; 
  &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="n"&gt;FILTER&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;category_id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
      &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nv"&gt;"cat_count"&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;OUTER&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; 
  &lt;span class="n"&gt;category_things&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;category_things&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;thing_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; 
  &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; 
  &lt;span class="n"&gt;cat_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Research Effort
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fps65nv3qqhr4s9uijqba.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fps65nv3qqhr4s9uijqba.png" alt="Researcher" width="800" height="302"&gt;&lt;/a&gt;&lt;br&gt;
For the record, the trail of research that went into the notes, that morphed into this article, on realisation that esoteric snippets of acquired knowledge are worth recording and sharing are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/76193289/django-many2many-query-to-find-all-things-in-a-group-of-categories/76193754?noredirect=1#comment134395967_76193754"&gt;Stack Overflow: Django&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/76208347/select-all-things-in-a-given-list-of-categories-or-more-categories/76210455#76210455"&gt;Stack Overflow: SQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://codesandbox.io/p/sandbox/django-m2m-test-cizmud"&gt;CodeSandbox: Experiments with simple models&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>django</category>
      <category>python</category>
      <category>sql</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Updating a Bokeh Graph in a Django Website</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Wed, 16 Mar 2022 03:30:53 +0000</pubDate>
      <link>https://forem.com/thumbone/updating-a-bokeh-graph-in-a-django-website-i2j</link>
      <guid>https://forem.com/thumbone/updating-a-bokeh-graph-in-a-django-website-i2j</guid>
      <description>&lt;p&gt;With our &lt;a href="https://dev.to/thumbone/adding-graphs-to-a-django-website-4f66"&gt;first simple graph on an existing Django page&lt;/a&gt;, the challenge is to update it when the data on the page updates.&lt;/p&gt;

&lt;p&gt;To recap: I have a Django page, on it is a table of data, and I have added a Bokeh histogram that summarises the frequency distribution of one of that table's columns. To get there we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Added some Bokeh JavaScript and css links to our Django template&lt;/li&gt;
&lt;li&gt;Installed Bokeh on the webserver, added the BokehApp to Django's INSTALLED_APPS.&lt;/li&gt;
&lt;li&gt;In the Django view (which loads and renders the template) created the graph (first a figure, then vbars, then the div and script components to add to template,&lt;/li&gt;
&lt;li&gt;Added the div and script components to our Django template.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And we have a simple histogram on the page. Nice.&lt;/p&gt;

&lt;p&gt;But this page, allows users to specify a number of filters, and click a refresh button, which requests new data (with these filters applied, using an AJAX request) and updates the table. &lt;/p&gt;

&lt;p&gt;It now needs to update the graph as well!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdhj8x6ijzbmx6zjsf9l.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdhj8x6ijzbmx6zjsf9l.gif" alt="Updating Graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating the data in a Bokeh graph (the options)
&lt;/h2&gt;

&lt;p&gt;Turns out, there is no easy, or obvious way to do that. Bokeh is full of features, and can update all right, any number of ways, but not this way, not easily, not in any way I could find in the documentation. What options does it provide? &lt;em&gt;At least&lt;/em&gt; these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run a &lt;a href="https://docs.bokeh.org/en/latest/docs/reference/server.html" rel="noopener noreferrer"&gt;Bokeh server&lt;/a&gt;. This is a server you can run which can run python code of your wanting. It's easy to run: &lt;code&gt;bokeh serve --show myapp.py&lt;/code&gt; (though on a live webserver would need to be installed as a service and whatever port(s) - port 5006 by default - it uses to communicate with Bokeh in the client must be opened on the firewall, the gateway and any reverse proxy etc.). In short, this is a very nice prospect and feature as it communicates over a &lt;a href="https://javascript.info/websocket" rel="noopener noreferrer"&gt;websocket&lt;/a&gt; which has a number of advantages over &lt;a href="https://www.w3schools.com/xml/ajax_intro.asp" rel="noopener noreferrer"&gt;AJAX&lt;/a&gt;. But, all the same, it's overkill for this trial. And parked. What I want is a way for  my client side JavaScript, when the AJAX call completes and a response is received, to update the Bokeh Graph. I don't want a whole new piece of infrastructure (just yet, not on the first date).
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1e4b0qlv74mzd55vavgf.png" alt="Parked!"&gt;
&lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://docs.bokeh.org/en/latest/docs/user_guide/bokehjs.html" rel="noopener noreferrer"&gt;BokehJS&lt;/a&gt;. Looks very promising. Basically a JavaScript interface to Bokeh (✔️). But the documentation is discouraging for this trial. The documentation boldly warns: &lt;em&gt;The BokehJS APIs is still in development and may undergo changes in future releases.&lt;/em&gt; and all the examples it provides basically see the three key Python steps we used (creating a figure, vbars and components) moving entirely client side into JavaScript. That has some appeal too. Alas it totally does not explain if and how one might, using JavaScript, provide new data to a Bokeh graph created and provided by server side Bokeh. So this too we park for now, and bookmark the idea for later. For this trial, I am seeking a way to update the graph we created in &lt;a href="https://dev.tourl"&gt;part 1&lt;/a&gt;.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7k9yj9cxv2wxirhoc1et.png" alt="ParkedAgain!"&gt;
&lt;/li&gt;
&lt;li&gt;Configuring the graph to use an &lt;a href="https://docs.bokeh.org/en/latest/docs/reference/models/sources.html?highlight=ajaxdatasource#bokeh.models.AjaxDataSource" rel="noopener noreferrer"&gt;AjaxDataSource&lt;/a&gt;. Rather than providing two lists of numbers as a data source for the graph, we can provide it with a URL and a polling interval. And it will pool back checking for new data to that URL. But no, oh so close, but not what I want. I don't want polled AJAX calls, I already have a user-requested AJAX call (filter criteria changed, new data requested). So this too gets parked. Not a bad feature for live updating of data (though the Bokeh Server is probably better) but not good for this trial.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwn4yo86c0597fnu6uhus.png" alt="AndParkedAgain!"&gt;
&lt;/li&gt;
&lt;li&gt;Using a &lt;a href="https://docs.bokeh.org/en/latest/docs/user_guide/interaction/callbacks.html" rel="noopener noreferrer"&gt;CustomJS&lt;/a&gt; callback. This is another brilliant feature. Server side we can write some custom JS and attach it to a client side element to call back to, on given events, like for example data changing! Yes. And no .... Alas It can be attached to a whole range of Bokeh events, but not to our event (the click-handler for my button), the one that triggers an AJAX call to get new data and update the table. That button! I can't attach custom JS to that button and its click event, only to Bokeh widgets that have the &lt;code&gt;js_on_change&lt;/code&gt; method (with which the callback is registered with that widget). Again, ever so close, but no prize. That said, we will come to this one later in this trial, it does have a use, even in this trial (clue: we can use it to register a JavaScript callback that adjusts the axes, ticks and labels, which is called when the data on the vbars is changed - but we still have to find a way to change that data, client-side).
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa2np9vprotyu8mifc3bh.png" alt="ParkedForTheLastTime"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And with that (four exhaustively research options parked), we throw our hands up in the air, file a &lt;a href="https://stackoverflow.com/questions/71191122/bokeh-generate-graph-server-side-update-graph-from-js-client-side-change-data" rel="noopener noreferrer"&gt;Stack Overflow plea for help&lt;/a&gt;, and hope for the best. But, no answers arriving in any hurry and wanting to soldier on, we are left with no options but to reverse engineer something (which is technical jargon for working out how to do something in lieu of documentation or mentorship)&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse engineering Bokeh (client-side)
&lt;/h2&gt;

&lt;p&gt;Scratching my head, I was wondering "if, client-side, I have new data from AJAX call to my server, and I have a Bokeh histogram, how can I tell that histogram that I have new data and give it that data?" That was the crux of the problem that, in all my reading, and research, to that point, I had not found an answer to.&lt;/p&gt;

&lt;p&gt;The first, baby-step, in reverse engineering, of course, is to  take a look at the code. Fortunately, in nay modern browser, I can now press F12 and up propose the source code for the page and a wonderful suite of debugging tools. My, how easy things have become since the 1980s when I first started reverse engineering games by disassembling them to strip the copy protection from them (looks furtively around the room for any disapproving glares, and hopes the &lt;a href="https://lawpath.com.au/blog/is-there-a-statute-of-limitations-in-australia" rel="noopener noreferrer"&gt;statute of limitations&lt;/a&gt; is in full swing).&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iw841f98iae71pyedz2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iw841f98iae71pyedz2.png" alt="Reverse Engineering"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now if you recall, a web page with a Bokeh graph on it has a &lt;strong&gt;div&lt;/strong&gt; and &lt;strong&gt;script&lt;/strong&gt; provided by &lt;code&gt;bokeh.embed.components&lt;/code&gt; and injected into the page using our Django template.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: That div
&lt;/h3&gt;

&lt;p&gt;Here's what the div looks like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bk-root"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"591aa428-0ac3-4085-938e-481caf339309"&lt;/span&gt; &lt;span class="na"&gt;data-root-id=&lt;/span&gt;&lt;span class="s"&gt;"1233"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;wherein, we see our first two clues. Clearly an &lt;code&gt;id&lt;/code&gt; that the Bokeh JavaScript library will use to find the div (and I'm betting at this point the &lt;strong&gt;script&lt;/strong&gt; contains a reference to it) and a &lt;code&gt;data-root-id&lt;/code&gt; that sounds a lot like a lead in our hunt for a way to furbish this &lt;strong&gt;div&lt;/strong&gt; with new data!&lt;/p&gt;

&lt;p&gt;Searching that page for that &lt;code&gt;id&lt;/code&gt; surprise, surprise it crops in the script, under:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;render_items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;docid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;e71600f9-95b3-4b84-9210-eff6b3036e17&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root_ids&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1233&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;roots&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1233&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;591aa428-0ac3-4085-938e-481caf339309&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}}];&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Where we see the &lt;code&gt;data-root-id&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; bound once again. &lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: That script
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;script&lt;/strong&gt; itself yields none more clue. It starts with:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/javascript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;Bokeh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safely&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;which tells us, there's a JavaScript object called Bokeh, that we might interface with. And what better way to explore that object than to use the browsers debugger.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: That Bokeh object
&lt;/h3&gt;

&lt;p&gt;Perhaps the most straightforward way to catch JavaScript in action, after the whole page has loaded and everything has rendered and, it's in a clear run state is to set an &lt;a href="https://firefox-source-docs.mozilla.org/devtools-user/debugger/set_event_listener_breakpoints/index.html" rel="noopener noreferrer"&gt;Event Listener Breakpoint&lt;/a&gt; on the mouse-click event.&lt;/p&gt;

&lt;p&gt;Doing that, and then clicking on the Bokeh graph (I elected to click on the Refresh Tool button that the graph provides) breaks in the debugger, and there's a wonderful list of the variables, and under Global we see the Bokeh object.&lt;/p&gt;

&lt;p&gt;By expanding it and looking at all its many many properties with patience a small clue might be found. But we might find faster clues by looking at and following the code. &lt;/p&gt;

&lt;p&gt;We broke in an event handler:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;documentClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;to be exact. F9 is the debugger's Step key, so pressing F9 a few times and watching where we go may yield fruit. In fact, 11 F9 presses and we find outselves here:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;throttled_paint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;throttle_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;throttle&lt;/span&gt;&lt;span class="p"&gt;)(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repaint&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And that, is why I thought clicking the refresh Tool button might be a fine idea. There it is. &lt;code&gt;this.repaint&lt;/code&gt;. But what is &lt;code&gt;this&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Easy, we just type &lt;code&gt;this&lt;/code&gt; into the console and see that it's:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;PlotView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;removed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Signal0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_ready&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_slots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;WeakMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_idle_notified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Bingo!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: That PlotView
&lt;/h3&gt;

&lt;p&gt;Now PlotView sounds good. In the console in fact I type &lt;code&gt;this.&lt;/code&gt; and after teh &lt;code&gt;.&lt;/code&gt; the debugger intellisense predictor throws up a list of all the attributes of that object. I flick down them, and there's one that catches my eye &lt;code&gt;model&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;So &lt;code&gt;this.model.&lt;/code&gt; and voila it has an id, &lt;code&gt;this.model.id&lt;/code&gt; and it is ... 1233! yes, that is the &lt;code&gt;data-root-id&lt;/code&gt; of our div.&lt;/p&gt;

&lt;p&gt;Getting warm now. &lt;/p&gt;

&lt;p&gt;Looking at the Bokeh object I play hte same game. &lt;code&gt;Bokeh.&lt;/code&gt; throws up a long list of attributes again, and I page down, and one catches my eye again. It is &lt;code&gt;documents&lt;/code&gt;. Bokeh.documents seems to be an Array with one entry so &lt;code&gt;Bokeh.documents[0].&lt;/code&gt; lets us walk through the attributes of a document and voila, there is &lt;code&gt;Bokeh.documents[0]._all_models&lt;/code&gt; and it has an interesting value:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1233&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Toolbar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1250&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;PanTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1251&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;WheelZoomTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1252&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;BoxZoomTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; …&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;32 entries and the first one is our plot. So let's try:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Bokeh.documents[0].get_model_by_id(1233)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;no luck. How about:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Bokeh.documents[0].get_model_by_id("1233")&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and that looks good, it's a Plot object so lets try:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;Bokeh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get_model_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1233&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;which is &lt;code&gt;true&lt;/code&gt;! So we have a way of getting models in Javascript!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Finding the data source
&lt;/h3&gt;

&lt;p&gt;Alas, the Plot object has no eaisly found data source. Poking around inside of it I do find it has &lt;code&gt;renderers&lt;/code&gt; again a list with one entry, and so exploring &lt;code&gt;this.renderers[0]&lt;/code&gt; I see it has a &lt;code&gt;data_source&lt;/code&gt; property, which in turn has a &lt;code&gt;data&lt;/code&gt; property and, you get the idea ... &lt;code&gt;this.model.data_source.data&lt;/code&gt; is on the money:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data_source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;x&lt;/code&gt; are the very thinhgs I provided to &lt;code&gt;figure.vbar&lt;/code&gt; at the server end and each of these 12 value Arrays are the very lists I provided. Found!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: That data-root-id, again
&lt;/h3&gt;

&lt;p&gt;Recall, that in my Django view I built the graph as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.plotting&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;figure&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.embed&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;view_Events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Collect the categories and values
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;players&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Create the figure
&lt;/span&gt;    &lt;span class="n"&gt;plot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;x_axis_label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Count of Players&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;y_axis_label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Number of Events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;background_fill_alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;border_fill_alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pan,wheel_zoom,box_zoom,save,reset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# And the bars
&lt;/span&gt;    &lt;span class="n"&gt;bars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vbar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Build the context variables
&lt;/span&gt;    &lt;span class="n"&gt;graph_script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;graph_div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Add them to context
&lt;/span&gt;    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph_script&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;graph_script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph_div&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;graph_div&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Render the view
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;events.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I checked &lt;code&gt;plot&lt;/code&gt; and sure enough it has an &lt;code&gt;id&lt;/code&gt;. And &lt;code&gt;plot.id&lt;/code&gt; here is exaclty the same value that appears in the data-root-id of the &lt;strong&gt;div&lt;/strong&gt; and is the id of the Plot object that was repainting itself when we walked through the click handler. The threads come together. &lt;/p&gt;

&lt;h3&gt;
  
  
  Changing the data source
&lt;/h3&gt;

&lt;p&gt;Sure enough if I pass &lt;code&gt;plot.id&lt;/code&gt; into context of the template and use client side it can find the Plot using that id. &lt;/p&gt;

&lt;p&gt;And so on the client side I have:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Bokeh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get_model_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plotid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data_source&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;players&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;where &lt;code&gt;plotid&lt;/code&gt; came in during page load via the Django template context, and &lt;code&gt;players&lt;/code&gt; and &lt;code&gt;frequency&lt;/code&gt; are delivered by the AJAX call.&lt;/p&gt;

&lt;p&gt;And that sort of works. The values are set.&lt;/p&gt;

&lt;p&gt;But the graph does not change. Hmmmm&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: What's wrong?
&lt;/h3&gt;

&lt;p&gt;I can see the data source is changed. Why hasn't the graph changed? I'm missing something. &lt;/p&gt;

&lt;p&gt;Scratching my head a little, I think, the missing link must be that repaint command. That is, we have given the Plot new data, but we need to tell it that it has new data, tell it that it needs to redraw itself somehow.&lt;/p&gt;

&lt;p&gt;So I break in the code above and inspect the &lt;code&gt;source&lt;/code&gt; variable for clues. And the very first attribute on its list is &lt;code&gt;change&lt;/code&gt;, which rings a loud bell. It has the value:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;Signal0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnDataSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And so is a singal of some sort, that we might send to the Plot. Inspecting the attributes of the signal we find it has (4th on its list), an &lt;code&gt;emit&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;So I add:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;to code above to get:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Bokeh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get_model_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plotid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;renderers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data_source&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;players&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And voila, now the graph updates. We hit paydirt!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Do we really need renderers?
&lt;/h3&gt;

&lt;p&gt;Not real comfortable with the use of &lt;code&gt;renderers&lt;/code&gt; and at this stage seeing that there are a lot of models ...:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;Bokeh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;_all_models&lt;/span&gt;
&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1233&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Plot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Toolbar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1250&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;PanTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1251&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;WheelZoomTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1252&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;BoxZoomTool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;32 models and among them when I expand the list are Vbars!&lt;/p&gt;

&lt;p&gt;So I try passing not plot.id into the context, but &lt;code&gt;bars.id&lt;/code&gt; and the AJAX response code becomes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Bokeh&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get_model_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;barsid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data_source&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;players&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;top&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And it works beautifully. No renderers needed. The connection is clear now too. At the python side I pass the x/y data to vbar() and at thye JavaScript side I get the vbar and change it's data source and emit a change signal ...all works wonderfully.&lt;/p&gt;

&lt;p&gt;The only thing I remain mildly uncomfortable about is the &lt;code&gt;documents[0]&lt;/code&gt;. I'm stil not clear what documents are, when there might be more than one and so on.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.freepik.com/vectors/car" rel="noopener noreferrer"&gt;Car vectors graciously created and made available by macrovector - www.freepik.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bokeh</category>
      <category>django</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Adding Graphs to a Django Website</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Thu, 10 Mar 2022 03:21:35 +0000</pubDate>
      <link>https://forem.com/thumbone/adding-graphs-to-a-django-website-4f66</link>
      <guid>https://forem.com/thumbone/adding-graphs-to-a-django-website-4f66</guid>
      <description>&lt;p&gt;Another journey, captured in my journal, that I think is worth sharing. To wit, a story (of learning and how that proceeds), and a summary (of how to add graphs to a Django website).&lt;/p&gt;

&lt;h2&gt;
  
  
  The story
&lt;/h2&gt;

&lt;p&gt;I have a &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt; website on which I am presenting a table of data and some statistics on that table (maxima, minima, medians etc.). I wanted to add a histogram (a bar chart) that would summarise the frequency distribution of a value in one column of the table.&lt;/p&gt;

&lt;p&gt;If you struggle with abstractions, I can help a little with specifics too. In my case, it is a table of events, and one of the columns records how many people attended the event. The histogram then would have the number of attendees across the bottom and each bar represent how often that number of attendees was recorded.&lt;/p&gt;

&lt;p&gt;This is, in fact, just such a histogram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3ic9ljy7yxjyflh28op.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz3ic9ljy7yxjyflh28op.png" alt="A sample histogram" width="636" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;taken from a table that list events and has a count of attendees (players, as they are gaming events). &lt;/p&gt;

&lt;p&gt;Now the existing page (no graph, just a table of events and some summary statistics) has a set of filters on it, so that you can look at a subset of all events - based on weekdays, or venue, or club, or host, or league, or duration or attendee counts etc ... A set of filters can be chosen, and a refresh button clicked and voilà, the data is updated in the blink of an eye (with an AJAX call back to the server that fetches data and updates the relevant page elements).&lt;/p&gt;

&lt;p&gt;And that, then, is the context, the desire to plug a graph into such an existing Django page. And as usual, it proved to be, an adventure, with notes taken, and one worth sharing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing a tool
&lt;/h3&gt;

&lt;p&gt;Such a journey starts with an idea and no rush. At least on this project it does, quite in contrast to many projects, notably those that need to monetise rapidly or beat the competition to market. But this project is a backburner on which notes are taken, and jobs tackled as time permits. &lt;/p&gt;

&lt;p&gt;And it starts with the idea, and over time the idea collected three candidates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dash.plotly.com/" rel="noopener noreferrer"&gt;Plotly Dash&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.flotcharts.org/" rel="noopener noreferrer"&gt;Flot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.bokeh.org/en/latest/" rel="noopener noreferrer"&gt;Bokeh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Long story short, this list is not exhaustive, there will be more out there, and each of these tools takes a little reading and comparing to arrive at a choice. &lt;/p&gt;

&lt;p&gt;Flot loses ground early, as it's jQuery dependence in an era of jQuery decline (in popularity) didn't strike me as an investment into the future. &lt;/p&gt;

&lt;p&gt;Dash and Bokeh go head-to-head in a number of online comparisons, the best of which I found was &lt;a href="https://pauliacomi.com" rel="noopener noreferrer"&gt;Paul Iacomi&lt;/a&gt;'s:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pauliacomi.com/2020/06/07/plotly-v-bokeh.html" rel="noopener noreferrer"&gt;https://pauliacomi.com/2020/06/07/plotly-v-bokeh.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I opted for Bokeh in the end based on those comparisons. You can spend a lot of time comparing a lot of alternatives, of course, and always end up taking a gamble and then investing in one. If you have more free time or are paid to, you can go one further and try several of them, compare them, and write up your experiences. I don't, and so I did some basic reading and said: Bokeh, you're the one.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbu0fu5ga5zbkm8nna6w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjbu0fu5ga5zbkm8nna6w.png" alt="You're The One!" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The first graph
&lt;/h3&gt;

&lt;p&gt;Bokeh themselves, provide a lovely little sample for a histogram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.bokeh.org/en/latest/docs/gallery/bar_basic.html" rel="noopener noreferrer"&gt;https://docs.bokeh.org/en/latest/docs/gallery/bar_basic.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there are a number of online pages running through the basic steps of a Bokeh graph in Django ranging from the, IMHO, &lt;a href="https://hackernoon.com/integrating-bokeh-visualisations-into-django-projects-a1c01a16b67a" rel="noopener noreferrer"&gt;bad&lt;/a&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt;, to the &lt;a href="https://www.geeksforgeeks.org/integrating-bokeh-visualizations-in-django-application/" rel="noopener noreferrer"&gt;OK&lt;/a&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;. &lt;/p&gt;

&lt;p&gt;But, assuming you have a Django site already (my situation and if you're reading here, possibly yours too - &lt;a href="https://en.wikipedia.org/wiki/Cf." rel="noopener noreferrer"&gt;cf.&lt;/a&gt; the title) it is worth summarising the needs.&lt;/p&gt;
&lt;h3&gt;
  
  
  Getting the basics into place
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You'll need to install the Python libraries - in whatever context your Django (backend) site is running in, of course. It's as simple as &lt;code&gt;pip install bokeh&lt;/code&gt; &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You'll need to register Bokeh with Django as an app. Add &lt;code&gt;BokehApp&lt;/code&gt; to (the end of) your &lt;a href="https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-INSTALLED_APPS" rel="noopener noreferrer"&gt;&lt;code&gt;INSTALLED_APPS&lt;/code&gt;&lt;/a&gt; setting. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your view will need to provide a few things in context to the Django template for rendering. Specifically, and at least, to things you will produce in that view using Bokeh (more on that later):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A div string&lt;/strong&gt;: Just a string that contains an HTML &lt;a href="https://www.w3schools.com/Tags/tag_div.asp" rel="noopener noreferrer"&gt;div&lt;/a&gt; element that will house the graph itself. You can give it any name you like in the context, but if you called it &lt;code&gt;bokeh_graph_div&lt;/code&gt; you'd simply stick it in your template as &lt;code&gt;{{ bokeh_graph_div | safe }}&lt;/code&gt; where you want the graph to be.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A script string&lt;/strong&gt;: Just a string that contains an HTML &lt;a href="https://www.w3schools.com/Tags/tag_script.asp" rel="noopener noreferrer"&gt;script&lt;/a&gt; element which you put anywhere in your template (though it's customary to put these at the start or end of the template I guess). This is the script that will actually draw the graph onto a canvas it places in the div. Again, if you called this &lt;code&gt;bokeh_graph_script&lt;/code&gt; in your context you'd include it in the template with &lt;code&gt;{{ bokeh_graph_script | safe }}&lt;/code&gt; in your template. Note though this is just a tiny hook script, the bulk of the code that does that drawing and more, is in JavaScript libraries that you will need to include in your template as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your template will need to provide a few things too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It will need to include the &lt;strong&gt;div&lt;/strong&gt; and &lt;strong&gt;script&lt;/strong&gt; strings described above.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It will need to include style sheets for the Bokeh elements. Basic, recommendation is to include a basic Bokeh style sheet, and another for its widgets and to source them from a &lt;a href="https://en.wikipedia.org/wiki/Content_delivery_network" rel="noopener noreferrer"&gt;CDN&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://cdn.pydata.org/bokeh/release/bokeh-2.4.2.min.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://cdn.pydata.org/bokeh/release/bokeh-widgets-2.4.2.min.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;It will need to include the JavaScript that actually draws the graph and provides all of its features.  Four includes seem commonly recommended:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.bokeh.org/bokeh/release/bokeh-api-2.4.2.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;With all that in place, you actually have a graph! But for one thing, the details of generating its content, the &lt;strong&gt;div&lt;/strong&gt; and &lt;strong&gt;script&lt;/strong&gt; mentioned earlier.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defining the graph's content
&lt;/h3&gt;

&lt;p&gt;For a histogram, you'll need to prepare your data in two parallel lists one for the categories (X axis) and one for the values (Y axis).&lt;/p&gt;

&lt;p&gt;In pro forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;categories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where these two lists have the same length.&lt;/p&gt;

&lt;p&gt;Then there are two objects of interest to create (the &lt;strong&gt;div&lt;/strong&gt; and &lt;strong&gt;script&lt;/strong&gt;), left.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a &lt;a href="https://docs.bokeh.org/en/2.4.0/docs/reference/plotting/figure.html" rel="noopener noreferrer"&gt;figure&lt;/a&gt; which will hold the graph. This can be as simple as:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.plotting&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;figure&lt;/span&gt;
&lt;span class="n"&gt;my_figure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;code&gt;bokeh.plotting.figure&lt;/code&gt; has a pile of options you can play with of course, for sizing, colouring, gridding, labelling and more. What it doesn't deal with is data. It's like the backdrop into which we can now drop our data somehow.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Plonk your data onto the figure. Here I'm interested in a histogram and the &lt;a href="https://docs.bokeh.org/en/2.4.0/docs/reference/models/glyphs.html?highlight=glyph" rel="noopener noreferrer"&gt;glyph&lt;/a&gt; of choice is the &lt;a href="https://docs.bokeh.org/en/2.4.0/docs/reference/models/glyphs/vbar.html" rel="noopener noreferrer"&gt;vbar&lt;/a&gt; and dropping it into the figure can be as simple as:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;bars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;my_figure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vbar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cateories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Build the Django context variables. There's a single method (&lt;a href="https://docs.bokeh.org/en/2.4.0/docs/reference/embed.html?highlight=bokeh%20embed%20components#bokeh.embed.components" rel="noopener noreferrer"&gt;bokeh.embed.components&lt;/a&gt;) that will create them for you from your figure:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.embed&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;
&lt;span class="n"&gt;bokeh_graph_script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bokeh_graph_div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_figure&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;and then in your view, just add them to the context delivered to the template. &lt;a href="https://djangotherightway.com/django-templates" rel="noopener noreferrer"&gt;A good tutorial&lt;/a&gt; will help, but I'm assuming you have a Django site you're dropping this into and so it might simply resemble:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bokeh_graph_script&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bokeh_graph_script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bokeh_graph_div&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;bokeh_graph_div&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now you will see a lovely histogram of your values vs. categories in the page where you put the &lt;strong&gt;div&lt;/strong&gt;. And you can look at the options for &lt;a href="https://docs.bokeh.org/en/2.4.0/docs/reference/plotting/figure.html" rel="noopener noreferrer"&gt;&lt;code&gt;bokeh.plotting.figure&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://docs.bokeh.org/en/2.4.0/docs/reference/plotting/figure.html?highlight=vbar#bokeh.plotting.Figure.vbar" rel="noopener noreferrer"&gt;&lt;code&gt;bokeh.plotting.figure.vbar&lt;/code&gt;&lt;/a&gt; to make it look nice and the way you like. Oh, and heck if you want something more than a histogram there are plenty of other &lt;a href="https://docs.bokeh.org/en/2.4.0/docs/reference/models/glyphs.html?highlight=glyphs" rel="noopener noreferrer"&gt;glyphs to choose from&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  A specific example that worked for me
&lt;/h3&gt;

&lt;p&gt;With all that in mind an example I had running and liked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.plotting&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;figure&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bokeh.embed&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;view_Events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Collect the categories and values
&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;players&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Create the figure
&lt;/span&gt;    &lt;span class="n"&gt;plot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;x_axis_label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Count of Players&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;y_axis_label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Number of Events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;background_fill_alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;border_fill_alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                  &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pan,wheel_zoom,box_zoom,save,reset&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# And the bars
&lt;/span&gt;    &lt;span class="n"&gt;bars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vbar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;players&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Build the context variables
&lt;/span&gt;    &lt;span class="n"&gt;graph_script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;graph_div&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Add them to context
&lt;/span&gt;    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph_script&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;graph_script&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph_div&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;graph_div&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;# Render the view
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;events.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is in fact the code I used, with distracting details removed (as in, this view also contains a table of events and a table of stats and a pile of filtering controls and such which have been removed from this sample). Similarly, if I reduce the template &lt;code&gt;events.html&lt;/code&gt; by removing all that distracting detail it looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% extends "base.html" %}

{% block styles %}
     &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;”http://cdn.pydata.org/bokeh/release/bokeh-2.4.2.min.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;”stylesheet”&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;”text/css”&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;”http://cdn.pydata.org/bokeh/release/bokeh-widgets-2.4.2.min.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;”stylesheet”&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;”text/css”&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;   
{% endblock %}

{% block content %}
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Graph of Event Attendance&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    {{ graph_div | safe }}
{% endblock %}

{% block scripts %}
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.bokeh.org/bokeh/release/bokeh-2.4.2.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.2.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.2.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.bokeh.org/bokeh/release/bokeh-api-2.4.2.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    {{ graph_script | safe }}
{% endblock %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;base.html&lt;/code&gt; is a Django template with placeholders for &lt;code&gt;styles&lt;/code&gt;, &lt;code&gt;content&lt;/code&gt; and &lt;code&gt;scripts&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making it interactive
&lt;/h3&gt;

&lt;p&gt;Here, the adventure truly begins. Bokeh is a rich library with high interactivity touted as one of its assets. The central feature of that is being able to update data in the graph in response to user requests. &lt;/p&gt;

&lt;p&gt;Alas, it proved not to be so simple at all, very poorly documented, and requiring some reverse engineering. Which is precisely where a journal comes in handy and why it becomes useful to document.&lt;/p&gt;

&lt;p&gt;And this story is long enough so the next chapter will come ... next.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagsyx1mwp0rjtr7i39y3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fagsyx1mwp0rjtr7i39y3.jpg" alt="Coming soon" width="800" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Credits and Footnotes
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.freepik.com/photos/people" rel="noopener noreferrer"&gt;Pointing dude photo created by drobotdean - www.freepik.com&lt;/a&gt; &lt;br&gt;
&lt;a href="https://www.freepik.com/photos/flowers" rel="noopener noreferrer"&gt;Coming soon photo created by rawpixel.com - www.freepik.com&lt;/a&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;In case you don't like random judgements passed on websites, or, you're the unfortunate author of that one, here's why it's bad: The code blocks are all inconsistent in width and style and font size, the whole thing is as a consequence a jarring bother to try and read, and for my needs it repeats unnecessarily all the Django hoo-ha. A better page would just say "Set up a Django project" linking to another page that explains how - written by same author or someone else, they abound, and then get into the meaty stuff that the title promises, namely "Integrating Bokeh visualisations into Django Projects". Surprisingly enough if I'm drawn by that title (and I was), I know what a Django project is and I have one (or a few) and I'm looking to integrate Bokeh visualisations not have my hand held with clumsily formatted code examples through the basics of getting to where I already am, with a Django project. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;This one is &lt;em&gt;OK&lt;/em&gt; because the code blocks and images are at least in tune with the article. It's short of &lt;em&gt;Great&lt;/em&gt; because it falls behind an annoying login demand (which you can lose simply by disabling Javascript with &lt;a href="https://add0n.com/javascript-toggler.html" rel="noopener noreferrer"&gt;any old browser addon&lt;/a&gt; and reloading the page) and because it repeats the annoying handhold through setting up a Django site. Which is not annoying if that's what you need, but it's not what you need when you click on an article titled "Integrating Bokeh Visualisations in Django Application" - because if you click on that it's because you have a Django application and want to integrate Bokeh visualisation into it.  ↩&lt;/p&gt;

&lt;p&gt;I failed categorically to find a page I'd class as &lt;em&gt;Good&lt;/em&gt; alas - and admit I find the web, driven by this endless need for publishing and novelty and enormous population of wannabe writers clamouring for attention just does reruns of the same mediocre stuff over and over! &lt;/p&gt;

&lt;p&gt;As an astute reader you will, of course, have noted that I am writing on-line about my disappointment with the bulk of on-line writing and the deep irony of that. I do to. &lt;/p&gt;

&lt;p&gt;Of course, I am not clamouring for attention, not doing reruns of the same mediocre stuff, over and over, nor assuming with every story that you are feree of context and a total noob, while at he same time trying to ensure I link all jargon and enw ideas to explanatory pages. Nor am I begging you for your likes, subscribes or whatever - I am just happy to share stories and notes now and then as much to myself and for you, and because I believe that learning (and particularly retention) is driven by story, and there are enough dry facts out there, but my story is often one of finding dry facts the hard way (because I could not find any documentation on it) and involve exploration and often reverse engineering. These are the stories I most like having clearly recorded.&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>bokeh</category>
      <category>django</category>
      <category>python</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Bringing it all together: Copy With Style</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Thu, 06 Jan 2022 02:51:20 +0000</pubDate>
      <link>https://forem.com/thumbone/bringing-it-all-together-copy-with-style-2h91</link>
      <guid>https://forem.com/thumbone/bringing-it-all-together-copy-with-style-2h91</guid>
      <description>&lt;p&gt;And now the tour is done, all that is left is to present the Copy With Style interface and how it is used.&lt;/p&gt;

&lt;p&gt;We expose a single class &lt;code&gt;Copy_With_Style&lt;/code&gt; that can be instantiated as follows in client side Javascript. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clipboard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Copy_With_Style&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button_to_copy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                        &lt;span class="na"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;element_to_copy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                                        &lt;span class="na"&gt;stylesheets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;default.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                                      &lt;span class="p"&gt;});&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To clarify, this is yet more delectably clear and intuitive JavaScript notation (not)! It is the de facto standard all the same, for passing a list of optional, named arguments to a function (in this case, a &lt;a href="https://www.w3schools.com/Js/js_classes.asp"&gt;class&lt;/a&gt; constructor).&lt;/p&gt;

&lt;p&gt;It works using a JavaScript object which is an arbitrary container for attributes and is &lt;a href="https://afontcu.medium.com/cool-javascript-9-named-arguments-functions-that-get-and-return-objects-337b6f8cfa07"&gt;described well enough by others&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Important to note is only that in reality there is only one argument, which is an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object"&gt;object&lt;/a&gt; and can be written like so in Javascript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myobj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks a little like a &lt;a href="https://www.w3schools.com/python/python_dictionaries.asp"&gt;Python dictionary&lt;/a&gt; to me but hey in JavaScript it's an object but looks and works the same way more or less as a dictionary. That is, it can contain properties and be &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer"&gt;initialized&lt;/a&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myobj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;prop1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;prop2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;prop3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript is a little more flexible here and you could write also:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myobj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;myobj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;myobj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;myobj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prop3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you need to know is that the single object argument that &lt;code&gt;Copy_With_Style&lt;/code&gt; takes can have these attributes (default values are shown and if missing are mandatory arguments, or attributes or properties or whatever you like to call them):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Argument&lt;/th&gt;
&lt;th&gt;Default value&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;button&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;an &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Element"&gt;HTML element&lt;/a&gt; that can be clicked. Ideally a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button"&gt;button&lt;/a&gt; element. If it has a progress element as a child or sibling this can be used for monitoring &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress"&gt;progress&lt;/a&gt; on style in-lining. Unnecessary unless you are copying very large HTML elements with many 10s or 100s of thousands of children.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;element&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;an &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Element"&gt;HTML element&lt;/a&gt; that will be copied ()with all its children) to the clipboard when &lt;strong&gt;button&lt;/strong&gt; is clicked.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;stylesheets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array"&gt;array&lt;/a&gt; of strings that represent CSS style sheets. For example &lt;code&gt;["default.css", "extras.css"]&lt;/code&gt;. If this is a non empty array then only styles from these sheets will be included in the copy. if you know your element only draws styles from specific sheets, then specifying them can speed up the style inlining and/or shrink the size of the copy. Otherwise all the stylesheets the document includes will be used.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"attribute"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A string. Either "attribute" or "tag". &lt;br&gt;If "tag" then a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tag is added to the copy and the &lt;strong&gt;element&lt;/strong&gt;'s style attributes are left untouched.  This  is fast and can conserve pseudo elements like &lt;code&gt;:hover&lt;/code&gt;.  Most email clients can't cope with this, but it will produce a rich copy in HTML contexts that do.&lt;br&gt;If "attribute" then the style attributes of &lt;strong&gt;element&lt;/strong&gt; and all its children will be updated with style information taken from the &lt;strong&gt;stylesheets&lt;/strong&gt; and the browsers computed styles, to produce as true a copy as possible. This produces a larger copy generally than "tag" but is respected by most email clients today. It's also a lot slower to produce if your element is large enough canhave significant processing costs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;defer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[50000,0]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Meaningful only if &lt;strong&gt;mode&lt;/strong&gt; == "attribute" and determines if and how often the style in-liner will defer to to UI to keep the UI responsive. Possible values are:&lt;br&gt;&lt;code&gt;true&lt;/code&gt;: defer to the UI after ever element is processed. Not recommended, slows down processing immensely.&lt;br&gt;&lt;code&gt;false&lt;/code&gt;: never defer to the UI while in-lining. Will lock the UI until finished. No problem for small elements, can be bothersome for very large elements.&lt;br&gt;&lt;code&gt;[threshold, frequency]&lt;/code&gt;: The UI is deferred to only if more than &lt;code&gt;threshold&lt;/code&gt; elements are being copied, and only once ever &lt;code&gt;frequency&lt;/code&gt; elements are processed. If &lt;code&gt;frequency&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt; and a &lt;strong&gt;progress&lt;/strong&gt; bar is specified, it is is optimised to be number of elements per progress bar pixel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;triggers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;["button"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Meaningful only if &lt;strong&gt;mode&lt;/strong&gt; == "attribute" and determines how and when style in-lining is triggered. This is an array of triggers and can contain:&lt;br&gt;&lt;code&gt;"button"&lt;/code&gt;: to request that style in-lining happen when the copy &lt;strong&gt;button&lt;/strong&gt; is clicked.&lt;br&gt;&lt;code&gt;"schedule"&lt;/code&gt;: to schedule a style in-lining once the DOM is fully rendered.&lt;br&gt;&lt;code&gt;"observe"&lt;/code&gt;: to request that &lt;strong&gt;element&lt;/strong&gt; be observed, and if it's seen to change, then a style in-lining will be triggered. This is useful if element is responsive to user interactions. If &lt;strong&gt;defer&lt;/strong&gt; is set to maintain a responsive UI any change to &lt;strong&gt;element&lt;/strong&gt; will trigger a request for any existing in-lining to bail and start one anew.&lt;br&gt;Sensible combinations are:&lt;br&gt;&lt;code&gt;["button"]&lt;/code&gt; for small and moderate elements.&lt;br&gt;&lt;code&gt;["schedule", "observe"]&lt;/code&gt;  for extremely large elements.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;progress&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Meaningful only if &lt;strong&gt;mode&lt;/strong&gt; == "attribute" and requests that a progress bar be displayed to convey the progress of style in-lining. Accepts the following values:&lt;br&gt;&lt;code&gt;false&lt;/code&gt;: no progress bar is used.&lt;br&gt;&lt;code&gt;true&lt;/code&gt;: a progress bar is used if an HTML progress element is found as a sibling or child of &lt;strong&gt;element&lt;/strong&gt;.&lt;br&gt;an HTML progress element: specify an element if you prefer and it will be used. &lt;br&gt;If  a progress bar is being used then &lt;strong&gt;defer&lt;/strong&gt; is also set, to &lt;code&gt;[0, 0]&lt;/code&gt; if it is not set (&lt;code&gt;false&lt;/code&gt;) or the threshold is set to 0 if it is an Array of 2 elements. This is necessary because without a deferral to UI the progress bar will not update (render).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;copy_wrapper&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;element&lt;/strong&gt; is wrapped in a simple &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div"&gt;&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;&lt;/a&gt; with id &lt;code&gt;copy_me_with_style&lt;/code&gt; before styling (either by tag or attribute as specified by &lt;strong&gt;mode&lt;/strong&gt;). If &lt;code&gt;true&lt;/code&gt; the wrapper will be place on the clipboard, if &lt;code&gt;false&lt;/code&gt; only its contents will be (i.e &lt;strong&gt;element&lt;/strong&gt; and any style information added).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;class_button&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"copy_with_style"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The CSS class assigned to the provided button. This is the buttons rest state though it conserves this class across all states. When clicked in this state the button will trigger a copy preparation if necessary and a copy of prepared data to the clipboard.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;class_preparing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"preparing_for_copy"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The CSS class assigned to the provided button when copy preparation is in progress. This may be very very quick (near instantaneous) or take some while, depending on choice of &lt;strong&gt;mode&lt;/strong&gt; and size of the &lt;strong&gt;element&lt;/strong&gt;. Most things are very fast, but "attribute" mode with very large elements can be slow. When in this state the button will either be disabled or trigger a restart of the preparation depending on configuration.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;class_ready&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"ready_to_copy"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The CSS class assigned to the provided button when copy preparation is in complete and a copy is ready to place on the clipboard. When in this state the button will simply copy the prepared texts and HTML to the clipboard.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;deep_exclusions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Meaningful only if &lt;strong&gt;mode&lt;/strong&gt; == "attribute" and provides a function to call, which accepts an HTML element as its only argument, and returns true if that element and all its children should be excluded from the copy.&lt;br&gt;The default implementation excludes all hidden (not visible) elements. &lt;br&gt;If provided, this function replaces the default implementation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;shallow_exclusions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Meaningful only if &lt;strong&gt;mode&lt;/strong&gt; == "attribute" and provides a function to call, which accepts an HTML element as its only argument, and returns true if that element and and only that element should be excluded from the copy (its children are grafted onto the parent).&lt;br&gt;The default implementation excludes all all A tags that link internal to the site (href begins with /) and DIV tags that have the class "tooltip".&lt;br&gt;If provided, this function replaces the default implementation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;extra_deep_exclusions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Identical to &lt;strong&gt;deep_exclusions&lt;/strong&gt;,  except that it augments rather than replaces the default implementation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;extra_shallow_exclusions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Identical to &lt;strong&gt;shallow_exclusions&lt;/strong&gt;,  except that it augments rather than replaces the default implementation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;debug&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;true&lt;/code&gt;, debugging information will be written to the console. Useful for checking the scheduling and observation and copy event triggers and such. Was used in developing and tuning this little class and remains in place for future use.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;log_performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;true&lt;/code&gt; will log style in-lining performance to the console. This was used to arrive at the performance statistics discussed above.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;log_HTML_to_console&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;true&lt;/code&gt; will log the styled HTML to the console, where it can be inspected. Useful for debugging if pasting brings no joy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;log_text_to_console&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;true&lt;/code&gt; will log the styled text to the console, where it can be inspected. Useful for debugging if pasting brings no joy.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;check_clone_integrity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When adding styles &lt;strong&gt;element&lt;/strong&gt; is cloned and it is this clone that is styled and added to the clipboard. &lt;code&gt;true&lt;/code&gt; request that after cloning its integrity is checked. It's never failed, and there's no reason it should, and this is unlikely to be of any great use.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;classes_to_debug&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An array of CSS &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/class"&gt;class&lt;/a&gt; names. If specified will break in the browser debugger during style in-lining when an element with one of the named classes is being processed. A nice way to drill down to specific classes to inspect the JavaScript variables if for any reason in-lining is not producing joy for a given class.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;styles_to_debug&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An array of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/style"&gt;style&lt;/a&gt; names. If specified will break in the browser debugger during style in-lining when an element with one of the named styles being applied is being processed. A nice way to drill down to specific styles to inspect the JavaScript variables if for any reason in-lining is not producing joy for a given class. if &lt;strong&gt;classes_to_debug&lt;/strong&gt; are defined will break if both a class and style match are found. This can of course easily be tuned in code as needed.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;And that brings this 12 part series to a conclusion, having introduced a small JavaScript class/library, Copy With Style:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/bernd-wechner/Copy-with-Style/blob/master/README.md"&gt;https://github.com/bernd-wechner/Copy-with-Style/blob/master/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is currently 857 lines, albeit unminimised runs at 35kB thanks to what is hopefully clean documented code. It does drop to 16kB when minimised and comments all stripped:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.minifier.org/"&gt;https://www.minifier.org/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://html-cleaner.com/js/"&gt;https://html-cleaner.com/js/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;though few minimisers I tried cope with the class definition (bizarrre).&lt;/p&gt;

&lt;p&gt;I hope it finds some use. After the &lt;a href="https://dev.to/thumbone/in-lining-styles-client-side-9oi"&gt;survey of existing options&lt;/a&gt; failed to provide a sensible one for a client side Copy button, I was stuck writing one and it was a journey.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Monitoring HTML Elements For Change</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Wed, 05 Jan 2022 11:32:38 +0000</pubDate>
      <link>https://forem.com/thumbone/monitoring-html-elements-for-change-336a</link>
      <guid>https://forem.com/thumbone/monitoring-html-elements-for-change-336a</guid>
      <description>&lt;p&gt;While trying to build a simple &lt;a href="https://dev.to/thumbone/copy-to-clipboard-first-cut-1j5j"&gt;clipboard copy button&lt;/a&gt;, I found the need to &lt;a href="https://dev.to/thumbone/in-lining-styles-client-side-9oi"&gt;in-line styles&lt;/a&gt; if they are to survive emailing, and that in-lining could, for very large elements be &lt;a href="https://dev.to/thumbone/html-style-in-lining-performance-and-what-to-about-it-4a55"&gt;rather costly&lt;/a&gt; (in time), of which was born a need to &lt;a href="https://dev.to/thumbone/deferring-to-the-user-interface-3fi9"&gt;let UI events happen&lt;/a&gt; while doing the job. But because my website is interactive, the element being copied might change while the styles are being in-lined!&lt;/p&gt;

&lt;p&gt;In the previous post I introduced the code base to which I begin to refer in these notes. That digression was needed to make sense of the ongoing notes as they refer in greater detail to actual implementation details.&lt;/p&gt;

&lt;h2&gt;
  
  
  What if an element changes while we're in-lining?
&lt;/h2&gt;

&lt;p&gt;A couple of things are worth noting here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;If I click the copy button, I want a copy of what we see on the browser now, after the change we made. But if the style in-liner is running when I click copy button, some most surprising things can happen, because Copy With Style is nice enough to &lt;code&gt;await defer_to_UI()&lt;/code&gt; at intervals I've managed to change the element while it's running. Turns out that if I time this right I can get a copy that is half how the element was, and half how it is. A kind of chimera element.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdweq9bwwoxxvmswitsl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhdweq9bwwoxxvmswitsl.jpg" alt="Chimera" width="300" height="197"&gt;&lt;/a&gt;&lt;br&gt;
 &lt;br&gt;
To help visualise this, in a standard use-case of mine I might be presenting a whole pile of tables (they'd be leaderboards in games in case it's important, lists of players and scores), and I might want to highlight certain players, or add or remove columns from these boards and the site has plenty of options to do just that. If I make such a change within 40 odd seconds of the page being presented (during which the initial render of a very large number of such leaderboards is having their styles in-lined ready to arm a copy button) and then click the copy button, I get this chimera of a copy. The first half before the change and the last half after depending on exactly when I change options, or how many times even.&lt;br&gt;
 &lt;br&gt;
So we need to watch the element for changes, and if it changes, we have to start style in-lining process a new.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If we're making changes though and every change we make kick fires a new in-lining of styles, we could see an explosion of in-lining jobs on the event queue. And so we need not just to start style in-lining again we also need some way of telling any running style in-liner to finish, quit, stop, bail.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmati5ui08dqp8mifw704.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmati5ui08dqp8mifw704.jpg" alt="Proceed no further" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Watching an element for changes
&lt;/h3&gt;

&lt;p&gt;A &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver"&gt;MutationObserver&lt;/a&gt; is handy for this. We can create one with a handler, that is run every time an element changes. That handler can check to see if a style in-liner is running, and if so ask it to stop, and wait until it has, before starting a new style in-liner. &lt;/p&gt;

&lt;p&gt;In the implementation of &lt;code&gt;Copy_With_Style&lt;/code&gt;,  &lt;code&gt;#observe_element&lt;/code&gt; instantiates this MutationObserver and associates it with the element (&lt;code&gt;this.element&lt;/code&gt;). The observer is stored as &lt;code&gt;#observer&lt;/code&gt; and &lt;code&gt;#mutation_handler&lt;/code&gt; is the handler called when &lt;code&gt;#observer&lt;/code&gt; notices that &lt;code&gt;element&lt;/code&gt; changed.&lt;/p&gt;

&lt;p&gt;A little synchronisation is needed here. If scheduling the preparation for copy (i.e. in-lining of element styles) then we don't want to start watching for changes until the style in-liner has started (there will be a lot of them, changes, that is, to the element, while the page is being rendered). &lt;/p&gt;

&lt;p&gt;To wit, even if we're not scheduling a style in-lining when the DOM is ready, we do not want to start observing the element until the DOM is ready (to avoid a flood change observations). So two schedulers are implemented &lt;code&gt;#schedule_preparation&lt;/code&gt; which schedules the style in-liner to start when the DOM is ready and &lt;code&gt;#schedule_observation&lt;/code&gt; which schedules an observer to start when the DOM is ready. These are started by the &lt;code&gt;constructor&lt;/code&gt; based on the configured &lt;code&gt;triggers&lt;/code&gt; in Copy With Style.&lt;/p&gt;

&lt;h3&gt;
  
  
  Communicating and executing a bail and restart
&lt;/h3&gt;

&lt;p&gt;All the communications required (for one event-triggered function to request a waiting function to bail, and then waiting for it to do so, before starting it anew) takes place with properties of the &lt;code&gt;Copy_With_Style&lt;/code&gt; instance. &lt;/p&gt;

&lt;p&gt;Specifically it uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;#is_being_prepared&lt;/code&gt; which is set to true when the in-liner (&lt;code&gt;prepare_copy&lt;/code&gt;) starts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#is_prepared&lt;/code&gt; which is set to true when it's completed successfully and &lt;code&gt;this.HTML&lt;/code&gt; and &lt;code&gt;this.text&lt;/code&gt; are set and ready to copied to the clipboard.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#bail&lt;/code&gt; which is set by the observer when &lt;code&gt;this.element&lt;/code&gt; changes and the style in-liner is running (&lt;code&gt;#is_being_prepared&lt;/code&gt; is true), to ask it to drop what it's doing and bail.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#bailed&lt;/code&gt; is set by the style in-liner to let the observer know it's done that (which it was requested to do).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember that JavaScript is (essentially) single threaded and that this requires a to and fro dance with &lt;code&gt;await defer_to_UI()&lt;/code&gt; which takes on a broader meaning now, and could rightly be thought of, or called, &lt;code&gt;defer_to_others()&lt;/code&gt; as we use it for one function to communicate with another function.&lt;/p&gt;

&lt;p&gt;The observer when running must set &lt;code&gt;#bail&lt;/code&gt; then &lt;code&gt;await defer_to_UI()&lt;/code&gt; to re-queue itself and give the style in-liner a chance to run and and it in turn checks &lt;code&gt;#bail&lt;/code&gt; whenever it continues after an &lt;code&gt;await defer_to_UI()&lt;/code&gt;. If it sees &lt;code&gt;#bail&lt;/code&gt; set, it bails, setting &lt;code&gt;#bailed&lt;/code&gt; first then returns (and thus comes off the queue and is no longer awaiting, has stopped processing elements). Eventually the observer's turn comes up again and it can check if &lt;code&gt;#bailed&lt;/code&gt; is set. If not of course it can &lt;code&gt;await defer_to_UI()&lt;/code&gt; again as often as needed until it is. In practice I have never seen it not honoured immediately - when the observer defers to UI it goes to the end of the queue and by definition a callback to the awaiting style in-liner is already on the queue ahead of it. &lt;/p&gt;

&lt;p&gt;The only way that the observer might need a second check of &lt;code&gt;#bailed&lt;/code&gt; is if the in-liner fails to check &lt;code&gt;#bail&lt;/code&gt; and act on it when it is called back after it's deferral to UI. &lt;/p&gt;

&lt;p&gt;Currently that only issues a warning to the console if it's encountered, but I've not seen it happen yet and if it did happen it suggests a bug.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Copy With Style: JavaScript Curios</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Mon, 27 Dec 2021 11:53:47 +0000</pubDate>
      <link>https://forem.com/thumbone/copy-with-style-javascript-curios-3dgk</link>
      <guid>https://forem.com/thumbone/copy-with-style-javascript-curios-3dgk</guid>
      <description>&lt;p&gt;As I progress with the notes I took driving &lt;a href="https://dev.to/thumbone/the-thing-is-on-awesomeness-and-motivation-2i0j"&gt;The Thing Is ...&lt;/a&gt; and I approach completion, slowly I reach the point were we need to look at more specific code examples, and it is time therefore for the great unveiling ... The code rests on github:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/bernd-wechner/Copy-with-Style"&gt;https://github.com/bernd-wechner/Copy-with-Style&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and we will digress quickly from the&lt;a href="https://dev.to/thumbone/monitoring-performance-1l7i"&gt; exploration of detecting changes in the DOM&lt;/a&gt;, to introduce the basic implementation and cover some of the JavaScript curios uncovered in implementing it. These are notes I took while implementing it, meaning they constituted learning for me, and struck me as not very intuitive warranting a specific mention and memory jog).&lt;/p&gt;

&lt;h1&gt;
  
  
  A JavaScript Class
&lt;/h1&gt;

&lt;p&gt;It's convenient to capture feature-provision such as this (copying an HTML buffer to the system clipboard with styles intact) in a JavaScript Class. Convenient because classes encapsulate code and isolate it from the broader JavaScript environment providing a clean interface.&lt;/p&gt;

&lt;p&gt;JavaScript classes are fairly customary and familiar in modern languages, and Javascript offers them in a fairly familiar manner. There's a great introduction on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes"&gt;mozilla.org&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;The main surprise we touched on in this little library is that members are not declared in the customary manner. That is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Properties&lt;/strong&gt; need no &lt;code&gt;var&lt;/code&gt;, &lt;code&gt;const&lt;/code&gt; or &lt;code&gt;let&lt;/code&gt; are just assigned a value. In a truly Pythonesque manner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Methods&lt;/strong&gt; also need no &lt;code&gt;function&lt;/code&gt; in fact won't accept them. They are declared otherwise, just a functions are, but without the &lt;code&gt;function&lt;/code&gt; keyword. Go figure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have to admit, this is a most puzzling feature of JavaScript syntax if ever I have seen one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Private Members
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields"&gt;Private members&lt;/a&gt; are a relative (and welcome) newcomer to JavaScript, and are declared simply by using &lt;code&gt;#&lt;/code&gt; as the first character in the name of the property or method. Alas being relatively new this causes havoc with some of the &lt;a href="https://www.testim.io/blog/what-is-a-linter-heres-a-definition-and-quick-start-guide/"&gt;linters&lt;/a&gt;, &lt;a href="https://codebeautify.org/formatters-and-beautifiers"&gt;beautifiers&lt;/a&gt; and &lt;a href="https://www.javatpoint.com/javascript-code-editors"&gt;editors&lt;/a&gt; out there. Let's hope that goes away soon (confident it will). For the record I use &lt;a href="https://www.eclipse.org/ide/"&gt;Eclipse&lt;/a&gt; with &lt;a href="https://projects.eclipse.org/projects/tools.wildwebdeveloper"&gt;Wild Web Developer&lt;/a&gt;, the &lt;a href="https://atom.io/"&gt;Atom editor&lt;/a&gt; as beautifier and online JavaScript linters.&lt;/p&gt;

&lt;p&gt;Still privacy is a thing well worth requesting and respecting and one of features Python classes lack. It is a welcome additions to JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;this&lt;/code&gt; is a Thing
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;this&lt;/code&gt; is a  keyword with different meanings in different contexts. As far as we were concerned, inside a class it refers to the instance of the class. And so &lt;code&gt;this.property&lt;/code&gt; is how to refer to a property of this instance of the class. &lt;/p&gt;

&lt;p&gt;Unfortunately in event handlers, &lt;code&gt;this&lt;/code&gt; is an event instance, and if the handler is a class method, well ... when it refers to &lt;code&gt;this.property&lt;/code&gt; no joy is had, as this is an event and not an instance of the class. Python handles "this" much more elegantly in my opinion making the instance references explicit arguments to methods, but this is not a piece on Python.&lt;/p&gt;

&lt;p&gt;The solution in JavaScript is the &lt;code&gt;bind()&lt;/code&gt; method, which functions have, and which provides the value for &lt;code&gt;this&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;method&lt;/code&gt; passed as an event handler will have &lt;code&gt;this&lt;/code&gt; holding an event, not the class instance, but &lt;code&gt;method.bind(object)&lt;/code&gt; will have &lt;code&gt;object&lt;/code&gt; as &lt;code&gt;this&lt;/code&gt; when it runs. Of course, if we want it have the class instance as &lt;code&gt;this&lt;/code&gt; it's simply &lt;code&gt;method.bind(this)&lt;/code&gt; which is a kind of JavaScript custom it turns out.&lt;/p&gt;

&lt;p&gt;This (&lt;a href="https://en.wikipedia.org/wiki/Double_entendre"&gt;double entendre&lt;/a&gt; inescapable) is seen clearly in the &lt;code&gt;#copy_to_clipboard()&lt;/code&gt; method in Copy With Style but also in a couple of schedulers and an observer.&lt;/p&gt;

&lt;p&gt;This then is the heart of Copy With Style in a sense:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;copy_to_clipboard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`copy event handler triggered...`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboardData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HTML&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clipboardData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;copy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, it wants &lt;code&gt;this.HTML&lt;/code&gt; and &lt;code&gt;this.text&lt;/code&gt; which are Copy With Style instance properties set by &lt;code&gt;this.prepare_copy()&lt;/code&gt; and so we make the class instance available in &lt;code&gt;this&lt;/code&gt; by passing not &lt;code&gt;handler&lt;/code&gt; but &lt;code&gt;handler.bind(this)&lt;/code&gt; to the copy event listener.&lt;/p&gt;

&lt;p&gt;That is another JavaScript curio that takes some getting used to. The binding of this to this ... &lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;code&gt;async&lt;/code&gt; is a Thing Too
&lt;/h1&gt;

&lt;p&gt;The &lt;code&gt;async&lt;/code&gt; keyword is rather misleading alas. As JavaScript is essentially single threaded and it alludes to, well, &lt;a href="https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)"&gt;multithreading&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Multiprocessing"&gt;multiprocessing&lt;/a&gt; but it is not to be. As discussed above, all async does is flag a function as a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"&gt;Promise&lt;/a&gt;. That is, a function runs and returns, but an async function just returns having put the function on the end of an &lt;a href="https://javascript.info/event-loop"&gt;event loop queue&lt;/a&gt;. It will run then, as soon as possible after everything else that was queued runs. What this does is free the event loop up to run the functions already ahead of this newly queued async function.&lt;/p&gt;

&lt;p&gt;But if we &lt;code&gt;await&lt;/code&gt; the function when we call it, then it does not return immediately. It queues the async function as before, but not before telling the promise to call back right here when it's done. That is &lt;code&gt;await&lt;/code&gt; provides a means to continue running at this line as if we'd never left (state preserved), while permitting the queued promise to run (by saving its state and returning).&lt;/p&gt;

&lt;p&gt;To summarise, if we have two functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I'm normal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I'm done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;oddball&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I'm an oddball&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I'm done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then calls to these functions operate as follows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;result = normal()&lt;/code&gt; runs &lt;code&gt;normal()&lt;/code&gt; and returns when it's done. result contains "I'm done".&lt;/p&gt;

&lt;p&gt;&lt;code&gt;result = await oddball()&lt;/code&gt; puts &lt;code&gt;oddball()&lt;/code&gt; at the end of the event loop queue, and waits for its turn to come round, and for it to run and then returns with result containing "I'm done" (the mechanism of that wait is though, to save state, and register with the promise a call-back to this line with state intact when it's done - creating the local illusion of a blocking call while actually returning - and it's because await returns, and only returns a promise that it can only be used in &lt;code&gt;async&lt;/code&gt; functions, which are functions that return promises.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;result = oddball()&lt;/code&gt; puts &lt;code&gt;oddball()&lt;/code&gt; at the end of the event loop queue, and returns immediately, with &lt;code&gt;result&lt;/code&gt; now a Promise object. Not much use if we want its returned value. We can't get hold of it's returned value this way.&lt;/p&gt;

&lt;p&gt;We can though, get hold of the returned value of the function &lt;code&gt;oddball()&lt;/code&gt; ... which is "I'm done" by attaching a function to it via &lt;code&gt;.then(function_to_call_with_return_value_of_oddball_as_an argument)&lt;/code&gt; sort of like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;oddball().then(return_value =&amp;gt; { result = return_value })&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In case it's not clear this &lt;code&gt;return_value =&amp;gt; { result = return_value }&lt;/code&gt; is just an anonymous function, we could as well write the above line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;noname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;return_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;return_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;oddball&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;noname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will set the value of &lt;code&gt;result&lt;/code&gt; when &lt;code&gt;oddball()&lt;/code&gt; returns.&lt;/p&gt;

&lt;p&gt;There's one problem. If we don't give &lt;code&gt;oddball()&lt;/code&gt; a chance to run first it won't complete. For example if we do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nothing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;noname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;return_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;return_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;oddball&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;noname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then on the console we will see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nothing
I'm an oddball
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is, result, has the value "nothing" not the value that &lt;code&gt;oddball()&lt;/code&gt; returned ("I'm done") because we didn't give &lt;code&gt;oddball()&lt;/code&gt; a chance to run before we logged the result to the console. We need to pause our run, put ourselves on the event queue, let &lt;code&gt;oddball()&lt;/code&gt; run then if we check &lt;code&gt;result&lt;/code&gt; it will contain "I'm done". And that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nothing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;noname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;return_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;return_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;result_is_ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;oddball&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;noname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;result_is_ready&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;await&lt;/code&gt; is the "pause" that puts our continuation onto the end of the event queue, and returns, and (calling code willing) lets &lt;code&gt;oddball()&lt;/code&gt; run. To wit, this will produce on the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I'm an oddball
I'm done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wrote "Calling code willing" because &lt;code&gt;await&lt;/code&gt; returns, all it can do is cede control to whoever called the function it is in. it is not until this bubbled to the top of the JavaScript processing stack if you will that JavaScript is free to run tasks on the event queue.&lt;/p&gt;

&lt;p&gt;That is consider this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nothing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;outer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;noname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;return_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;return_value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;result_is_ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;oddball&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;noname&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;result_is_ready&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt; &lt;span class="c1"&gt;// An infinite - neverending - loop&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;outer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// JavaScript is done now.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;oddball()&lt;/code&gt; never runs, and it never calls back to the state-preserved &lt;code&gt;await&lt;/code&gt; because outer() never returns and the JavaScript engine remains occupied evermore with &lt;code&gt;while (true) {};&lt;/code&gt;. And while that's an extreme and artificial example, the point is that any code in place of that infinite loop or analogously placed after the promise was queued but running before JavaScript finished (like any code after &lt;code&gt;outer()&lt;/code&gt; and before &lt;code&gt;// JavaScript is done now.&lt;/code&gt;) will run before &lt;code&gt;oddball()&lt;/code&gt; ever does. Because JavaScript has to fall idle before it will take the next task off the queue. Running code always has the option and runs the risk, of hogging all of the JavaScript engine's attention!&lt;/p&gt;

&lt;p&gt;An voila! This may appear convoluted, and it certainly is a little, but the central idea is that a Promise schedules the function to run by placing it at the end of an event queue, and  when it runs, we can ask it to run a function of our choosing that receives its return value as an argument. But unless we take a break and pop ourselves onto the same event queue behind the promised function we will never see it fulfilled, this is the single-threaded nature of JavaScript on display.&lt;/p&gt;

&lt;p&gt;In summary, &lt;code&gt;async&lt;/code&gt; means a function no longer returns its return value, but instead returns a Promise, and the return value can be accessed either by using &lt;code&gt;await&lt;/code&gt; or by offering the promise a function to call with the return value as an argument (using &lt;code&gt;.then(function)&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;async&lt;/code&gt; chains 🔗
&lt;/h2&gt;

&lt;p&gt;Because an async function does not return the function's return value but a Promise object, to get the result we must &lt;code&gt;await&lt;/code&gt; it. Therein lies a small domino effect. &lt;code&gt;await&lt;/code&gt; can only be used in an &lt;code&gt;async&lt;/code&gt; function. That's nice little JavaScript rule there, a tautology even, as await returns a promise and &lt;code&gt;async&lt;/code&gt; is the keywords that flags a function as a promise returning function (and not a value returning function). &lt;/p&gt;

&lt;p&gt;Which all means that if we want a result from an &lt;code&gt;async&lt;/code&gt; function we can only get it in an &lt;code&gt;async&lt;/code&gt; function (i.e. we can only await a result in an async function). And so one async begets another and they chain ... and next thing you know it all your functions are &lt;code&gt;async&lt;/code&gt; ;-).&lt;/p&gt;

&lt;p&gt;In Copy_With_Style, this chain of &lt;code&gt;async&lt;/code&gt; bubbles all the way up to the button click handlers &lt;code&gt;copy()&lt;/code&gt; and &lt;code&gt;to_clipboard()&lt;/code&gt;. At which point we breathe a sigh of relief because the click handler can accept an &lt;code&gt;async&lt;/code&gt; function, in no small part because it really doesn't care about a return value.&lt;/p&gt;

&lt;p&gt;I see &lt;a href="https://stackoverflow.com/users/8427/jim"&gt;Jim&lt;/a&gt; provided &lt;a href="https://stackoverflow.com/a/128966/4002633"&gt;a little insight&lt;/a&gt; into that on &lt;a href="https://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt;. Namely that this area is not well documented and standardised to begin with (i.e. what the return value of event handlers does if anything). Either way we can take for granted and be thankful that the click handler is happy to have an async function. &lt;/p&gt;

&lt;p&gt;Still, what does that mean? Consider this, click the button and your handler is called and runs. That's the normal scenario. &lt;/p&gt;

&lt;p&gt;Now with an async function, click the button and the async function runs. But all that means is it puts the actual function on to the end of a event queue and it will run when its turn comes around. In short it doesn't run immediately and the return value isn't accessible in the same way. &lt;/p&gt;

&lt;p&gt;In practice this means very little because, as it happens, JavaScript has a dedicated queue for promises, and resolves all the code generated promises before it looks at UI events, which have their own queue. These are generally called the micro and macro task queues, with the macro queue only consulted when the micro queue is empty and the micro queue only consulted when the JavaScript engine falls idle (has nothing else to do), and promises generally occupying the micro queue and UI events the macro queue. &lt;/p&gt;

&lt;p&gt;Which is why, as we &lt;a href="https://dev.to/thumbone/deferring-to-the-user-interface-3fi9"&gt;discussed earlier&lt;/a&gt; and see in the implementation of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;defer_to_UI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;how_long&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;how_long&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A simple promise is not sufficient to see UI events processed, because promises are queued on the micro queue and executed before any UI events on the macro queue get to be handled. Which is where the browser supplied &lt;code&gt;setTimeout()&lt;/code&gt; function comes in which queues a function on the macro queue! In fact it should be names &lt;code&gt;PutOnMacroQueue(function)&lt;/code&gt; ...&lt;/p&gt;

&lt;h1&gt;
  
  
  The Object as an Argument
&lt;/h1&gt;

&lt;p&gt;Discussed under Bringing it All Together essentially the way JavaScript supports (or better said does not support) named function arguments with default values and optional provision by the caller (all Python does natively) it requires you to replace all arguments with a single object that has properties. And there's a syntax that is tolerable but fluffy. &lt;/p&gt;

&lt;h1&gt;
  
  
  The Width of &lt;code&gt;&amp;lt;progress&amp;gt;&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;I'm pleased to implement a &lt;code&gt;&amp;lt;progress&amp;gt;&lt;/code&gt; element here, as the lack of one had been a long standing gripe with HTML with umpteen 3rd party solutions. Finally standardised and stylable. It's great to see HTML and JavaScript evolving.&lt;/p&gt;

&lt;p&gt;To use it sensibly though we wanted to restrict updates to then and when it would cause a visible change, and so we needed a measure of elements to process (cycles in our function) per pixel of progress bar width. The former we know, the latter is tricky.&lt;/p&gt;

&lt;p&gt;Turns out it has no property to tells us that. The nearest thing available is &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/clientWidth"&gt;.clientWidth&lt;/a&gt; which strangely includes padding, and so we have to write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nf"&gt;bar_width&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;progress_bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getComputedStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;progress_bar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;progress_bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientWidth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paddingLeft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paddingRight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;removing the padding. But to make sure I got it right I checked the width this returned and took screenshots of the progress bar in Chromium and Firefox and lo and behold both returned the self same width (133 pixels) and I measured the bar on Firefox as 133 pixels (tick ✔) but Chromium renders it 146 pixels wide (bzzzt ✘). Go figure. Little can done about that, but it seems browser support in this space might be a little variable if, in the ideal, close to the mark. And for my purposes the smaller value (the consistently returned 133) is fine as not only is the difference small, this is the conservative value yielding more cycles per pixel and fewer updates.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>clipboard</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Monitoring Performance</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Mon, 13 Dec 2021 07:56:26 +0000</pubDate>
      <link>https://forem.com/thumbone/monitoring-performance-1l7i</link>
      <guid>https://forem.com/thumbone/monitoring-performance-1l7i</guid>
      <description>&lt;h2&gt;
  
  
  (a.k.a. How often shall we defer to the UI?)
&lt;/h2&gt;

&lt;p&gt;In my effort to build a simple &lt;a href="https://dev.to/thumbone/copy-to-clipboard-first-cut-1j5j"&gt;clipboard copy button&lt;/a&gt;, I found the need to &lt;a href="https://dev.to/thumbone/in-lining-styles-client-side-9oi"&gt;in-line styles&lt;/a&gt; if they are to survive emailing, and that in-lining could, for very large elements be &lt;a href="https://dev.to/thumbone/html-style-in-lining-performance-and-what-to-about-it-4a55"&gt;rather costly&lt;/a&gt; (in time), of which was born a need to &lt;a href="https://dev.to/thumbone/deferring-to-the-user-interface-3fi9"&gt;let UI events happen&lt;/a&gt; while doing the job. &lt;/p&gt;

&lt;p&gt;With all that under my belt, knowing how long it was likely to take would also be nice, a bit of UI feedback on progress of the style in-lining operation. Basically, a progress bar ...&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45rgp3d3h4itz35orh8t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45rgp3d3h4itz35orh8t.png" alt="Progress Bar" width="800" height="213"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Progress bars were once a problem in JavaScript, with lots of tips, tricks and 3rd party solutions. But no more, we now have the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress"&gt;progress&lt;/a&gt; element and it's a breeze. We can just add: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;progress value="0"/&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;to a button, or elsewhere, and style it as we wish, display it as needed and update it's &lt;code&gt;max&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt; to suit. The default width, it seems, when we put I  on my page was around the 150 pixels mark, but it can be styled as desired.&lt;/p&gt;

&lt;p&gt;And that answers our "How often shall we defer to the UI?" question implicitly. While not a fault-free, universally-suitable answer, it's not a bad start to update no more often than it takes to advance that progress bar by one pixel (i.e to make a visible change to it). &lt;/p&gt;

&lt;p&gt;For 100,000 elements say and a 150 pixel bar that's an ominous &lt;a href="https://en.wikipedia.org/wiki/666_%28number%29"&gt;666&lt;/a&gt; elements per pixel. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjq37izkf5p784xhr355x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjq37izkf5p784xhr355x.jpg" alt="666" width="600" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we drop the frequency of deferrals to the UI to once every 666 elements they are in-lined in &lt;a href="https://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker%27s_Guide_to_the_Galaxy#The_Answer_to_the_Ultimate_Question_of_Life,_the_Universe,_and_Everything_is_42"&gt;42&lt;/a&gt; seconds (an auspicious figure). Much improved performance.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F377av6uykw2oz2y4ui4s.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F377av6uykw2oz2y4ui4s.jpg" alt="42" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To recap: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;30 seconds with a locked UI, &lt;/li&gt;
&lt;li&gt;11 minutes if we defer to the UI after ever element (~100,000 times - i.e. 30 seconds of style processing an 10+minutes of promise management), and &lt;/li&gt;
&lt;li&gt;42 seconds if we temper the UI deferrals to 150 times over the duration of in-lining (for a 150pixel progress bar). &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Empirical tests confirm that, during those 42 seconds, the UI is admirably responsive, no noticeable lag and so we're content!&lt;/p&gt;

&lt;p&gt;But still, in 42 seconds the element being copied might change! That is, because we are deferring to the UI and maintaining responsive UI while in-lining styles, user events processed can change the element that we're copying! So we need to both know something has changed and start again if it has, but that is another chapter of &lt;a href="https://dev.to/thumbone/the-thing-is-on-awesomeness-and-motivation-2i0j"&gt;The Thing Is ...&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Deferring to the User Interface</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Thu, 25 Nov 2021 10:13:37 +0000</pubDate>
      <link>https://forem.com/thumbone/deferring-to-the-user-interface-3fi9</link>
      <guid>https://forem.com/thumbone/deferring-to-the-user-interface-3fi9</guid>
      <description>&lt;p&gt;When executing long-running JavaScript code the web browser's User Interface (UI) remains unresponsive thanks to the infamous single-threaded nature of JavaScript. Consequently it's useful in time-consuming JavaScript code, to defer back to the UI from time to time, to let user events like scrolling, clicking and typing all take their natural course.&lt;/p&gt;

&lt;p&gt;That proves to be quite a tricky thing. Or not, depending on how you look at it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/users/7385781/mario-figueiredo"&gt;Mario Figueiredo&lt;/a&gt; provides a &lt;a href="https://stackoverflow.com/a/60149544/4002633"&gt;solution&lt;/a&gt; on &lt;a href="https://stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; and I recast it here, essentially as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;defer_to_UI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;how_long&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;how_long&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have to love JavaScript no? Such abundant clarity not? (and I've taken liberties to clarify Mario's code a little too).&lt;/p&gt;

&lt;h1&gt;
  
  
  So what is actually going on here?
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;setTimeout&lt;/code&gt;, as it happens, schedules something to happen in the future by a specified number of milliseconds (&lt;code&gt;how_long&lt;/code&gt;) and if that is 0ms it schedules it to happen ASAP in the future. What does that mean?  &lt;/p&gt;

&lt;p&gt;Recalling that JavaScript is essentially single-threaded, what this does is put the call to &lt;code&gt;resolve()&lt;/code&gt; on a queue. The same queue as it happens that UI events are sitting in, waiting to be handled. &lt;/p&gt;

&lt;p&gt;In the context of the long running style in-lining code I'm running, they are not being handled as the styles are being in-lined. Because the in-lining function is running, they are waiting patiently in the queue until it's done. Which as you may recall &lt;a href="https://dev.to/thumbone/html-style-in-lining-performance-and-what-to-about-it-4a55"&gt;could be 30 seconds&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;setTimeout()&lt;/code&gt; is a function that asks the browser to run a timer (for &lt;code&gt;how_long&lt;/code&gt; microseconds), and when the timer is up, to place a call to the function (&lt;code&gt;resolve()&lt;/code&gt; in this case) on the event loop queue. But the function &lt;code&gt;resolve&lt;/code&gt; is a mystery here. It is provided as the argument to a function that is wrapped in a promise. Wowsers, what? &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5a3slf389g0bqxpjze7e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5a3slf389g0bqxpjze7e.jpg" alt="What?" width="460" height="300"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Promises, promises
&lt;/h1&gt;

&lt;p&gt;A promise is just a JavaScript object that maintains a state of either "pending", "fulfilled" or "rejected", with a few convenient callback hooks to set that state.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mn129ygaj4qnvg3gp2m.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mn129ygaj4qnvg3gp2m.jpeg" alt="Promise" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a Promise is instantiated it's state is initially "pending" and its constructor takes one argument, which must be a function. That  function is immediately executed, and given two arguments, also functions, the first of which must be called to set the state of the promise to "fulfilled" and the second of which must be called to set the state of the promise to "rejected". In a sense the argument to the constructor of a promise is the promised function - it is run, and to let the world know it succeeded it is asked to call the function it received as first argument, and to let the world know it failed it is asked to call the function provided as its second argument, and if it calls neither well ... we'll get to that (but no prize if you guess: the promise simply remains "pending").&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frin8mcbi2dcojk4nkrmu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frin8mcbi2dcojk4nkrmu.jpg" alt="Pending" width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To illustrate, a few examples are helpful.&lt;/p&gt;

&lt;p&gt;A promise that is immediately fulfilled (which isn't wildly useful) is created with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;new Promise(resolve =&amp;gt; resolve());&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That is the function just calls the first argument to flag the promise as "fulfilled". Note that this also uses JavaScript's &lt;a href="https://www.w3schools.com/Js/js_arrow_function.asp"&gt;arrow notation for functions&lt;/a&gt;. It can also be written any number of different ways using other notations (JavaScript is oh so flexible in that space it seems). Here are a few:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;new Promise(function(x) {x()});&lt;/code&gt; - note the name of the first argument is irrelevant. It's the promised function's argument, and can be called whatever you like. All that is important is that the promised function knows this argument is itself a function that it must call to tell the world that it has delivered on the promise - that it is done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deliver_me_my_promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;set_state_to_fulfilled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;set_state_to_fulfilled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deliver_me_my_promised&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is (literally) more colourful, as now we give the promised function a name, it's no longer anonymous, we've called it, oh so descriptively &lt;code&gt;deliver_me_my_promise()&lt;/code&gt; and its first argument has a revealing name too: &lt;code&gt;set_state_to_fulfilled&lt;/code&gt;. All it does is call &lt;code&gt;set_state_to_fulfilled()&lt;/code&gt; and that indeed is all that happens, the Promise object now has a state of "fulfilled"&lt;/p&gt;

&lt;p&gt;Of course, an immediately fulfilling promise isn't so useful. Nothing much happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  But, wait ...
&lt;/h2&gt;

&lt;p&gt;Enter &lt;code&gt;await&lt;/code&gt; - which is going to make it useful as we will shortly see.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmh4i5hb1krfodth1g17.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmh4i5hb1krfodth1g17.jpg" alt="Waiting" width="800" height="1027"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await&lt;/code&gt; is an operator, that waits on a Promise to fulfill (or reject, but let's ignore that for now). And so:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await new Promise(resolve =&amp;gt; resolve());&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;will do nothing, just return. The promise thus created, we just noticed, is fulfilled when it's created and &lt;code&gt;await&lt;/code&gt; checks to see if it's fulfilled and if so, returns. Which it does. This is in fact one of JavaScript's more interesting null statements, the equivalent of a &lt;a href="https://www.w3schools.com/python/ref_keyword_pass.asp"&gt;Python &lt;code&gt;pass&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But what if the promise is pending? That is, it has not yet called either its first or second argument. &lt;/p&gt;

&lt;p&gt;In that case &lt;code&gt;await&lt;/code&gt;, true to its name, does not return. It waits patiently (we'll get to how later).&lt;/p&gt;

&lt;p&gt;To illustrate though. we could write a promise that is perpetually pending easily enough, by not calling the provided resolver. Here's another useless promise, one that is never fulfilled and always pending:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;new Promise(resolve = {})&lt;/code&gt; - as it happens, &lt;code&gt;{}&lt;/code&gt; is the more common form of a JavaScript "do nothing" statement. Hang on, just for a laugh, you probably realised we could write this cute, always pending, promise using the fancier "do nothing" statement we just saw:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;new Promise(resolve = await new Promise(resolve = resolve()))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Pointless, and for good humour alone of course to define one promise in terms of another that does nothing.&lt;/p&gt;

&lt;p&gt;We could again write this in different ways like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deliver_me_my_promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;set_state_to_fulfilled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deliver_me_my_promise&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essentially the promise executor (&lt;code&gt;deliver_me_my_promise()&lt;/code&gt; in this case), the function it runs when it's created, never calls the provided resolver, never tells the Promise that it is "fulfilled" and so it sits there in a perpetual "pending" state. &lt;/p&gt;

&lt;p&gt;Now if we &lt;code&gt;await&lt;/code&gt; that:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await new Promise(resolve =&amp;gt; {});&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;await never returns. Just waits forever. This never-fulfilled promise is of course as useless as the immediately-fulfilled promise was. But helps to illustrate what &lt;code&gt;await&lt;/code&gt; does. &lt;/p&gt;

&lt;p&gt;As an aside, the means by which it waits is another topic for another day perhaps, but is often described as syntactic sugar around a yield/next structure, which means, in a nutshell only, that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the function that calls &lt;code&gt;await&lt;/code&gt; has its state saved (probably on the heap - where most stuff is stored), &lt;/li&gt;
&lt;li&gt;registers this fact with the promise (where it's remembered), returns (to the function calling it) and &lt;/li&gt;
&lt;li&gt;when it is next called it will continue at the line after the &lt;code&gt;await&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is next called as it happens, when the promised function calls either of the functions it received as arguments, the first to fulfill and the second to reject. &lt;/p&gt;

&lt;p&gt;These functions are provided by the promise and what they do when called, is set the state of the promise accordingly and call the function that is &lt;code&gt;await&lt;/code&gt;ing (remember that it registered its wait with the promise, and its state was saved so that on next call it continues on the line after the &lt;code&gt;await&lt;/code&gt;). This is the callback hook we referred to earlier, that is when the promised function calls either of its first two arguments, that sets the state of the promise to fulfilled or rejected respectively &lt;strong&gt;and&lt;/strong&gt; calls back any functions that registered themselves with the promise as "waiting".&lt;/p&gt;

&lt;h2&gt;
  
  
  More useful promises
&lt;/h2&gt;

&lt;p&gt;Your browser (through what's called its Web API) of course provides more useful promises (than our immediately and never fulfilled promises). &lt;a href="https://javascript.info/fetch"&gt;&lt;code&gt;fetch&lt;/code&gt;&lt;/a&gt; is such a useful built-in promise. It fetches a URL, which can take some time, so returns a promise. If you're happy to wait for the URL to return data then &lt;code&gt;await fetch(URL)&lt;/code&gt; is fine and &lt;code&gt;await&lt;/code&gt; will pause until it is no longer pending but fulfilled (or rejected). Of course if you don't want to wait for URL you can attach a callback to the promise using it's &lt;code&gt;.then()&lt;/code&gt; method as in: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;fetch(URL).then(call_this_function)&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;That simply registers &lt;code&gt;call_this_function&lt;/code&gt; with the promise, to be called when the promised function calls its first argument. Very much like &lt;code&gt;await&lt;/code&gt; registers the function it's in that way.&lt;/p&gt;

&lt;p&gt;Which means that &lt;code&gt;call_this_function&lt;/code&gt; won't be called until JavaScript is idle (aka the stack is empty or all your JavaScript functions have finished). Only then does the event loop look at this queue and call the  next thing in it (pulling it off the queue).&lt;/p&gt;

&lt;h2&gt;
  
  
  The most useful promise (to us here)
&lt;/h2&gt;

&lt;p&gt;We have come full loop, back to &lt;code&gt;setTimeout()&lt;/code&gt;. This is a native JavaScript function places a(nother) function call on the end of the event loop's queue and so this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;await new Promise(first_argument =&amp;gt; setTimeout(first_argument, 0));&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;creates a Promise that runs &lt;code&gt;setTimeout(first_argument, 0)&lt;/code&gt; which places a call to &lt;code&gt;first_argument()&lt;/code&gt; on the end of the event loop's queue. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;first_argument()&lt;/code&gt; sets the state of the promise to "fulfilled" and calls any functions that registered themselves with the promise earlier. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;await&lt;/code&gt; does just that, registers itself with the Promise requesting a call back to the same line and it registers that interest, perforce, before &lt;code&gt;first_argument()&lt;/code&gt; is called, because the call to &lt;code&gt;first_argument()&lt;/code&gt; is at the end of the event queue which JavaScript only starts processing now that we have given up control with &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So while JavaScript is running, &lt;code&gt;await&lt;/code&gt; registered it's desire to be called back when &lt;code&gt;first_argument()&lt;/code&gt; is called, and &lt;code&gt;first_argument()&lt;/code&gt; is called after all JavaScript has finished running, and all events queued ahead of the &lt;code&gt;first_argument()&lt;/code&gt; call on the event loop have been called (and completed). &lt;/p&gt;

&lt;p&gt;On the event loop, it's first-in best-dressed, and any UI events that were queued while JavaScript was running are dealt with (browser updates the rendered DOM) and then when they are done, the call to &lt;code&gt;first_argument()&lt;/code&gt; (that &lt;code&gt;setTimeout()&lt;/code&gt; put there) eventually runs and we carry on where we left off and the UI events have been dealt with. &lt;/p&gt;

&lt;p&gt;Of course we tend to name that first argument &lt;code&gt;resolve&lt;/code&gt; to produce: &lt;code&gt;await new Promise(resolve =&amp;gt; setTimeout(resolve, 0));&lt;/code&gt;. It doesn't matter what it's called, the promise just provides a call back function as the first argument and the name is a conventional mnemonic to remind us this is the function to call, to let the promise know it is fulfilled and to call any registered callbacks.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwx0lo40mnzv3nt6y36by.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwx0lo40mnzv3nt6y36by.png" alt="Image description" width="500" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When the &lt;code&gt;Promise&lt;/code&gt; is instantiated it runs the function provided as its only argument - the nameless function &lt;code&gt;function noname(resolve) { setTimeout(resolve, 0) }&lt;/code&gt; immediately.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setTimeout&lt;/code&gt; then puts a call to &lt;code&gt;resolve()&lt;/code&gt; on the end of a event loop queue, behind any waiting UI events. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;await&lt;/code&gt; pauses until &lt;code&gt;resolve&lt;/code&gt; is called, which does not happen until after all the UI events that were queued before it are called (and handled)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  More on pausing
&lt;/h2&gt;

&lt;p&gt;But what does "pausing" mean here? This is a curio worth understanding too, because the event loop queue is not processed until JavaScript is done. So how does it come to be done if it's paused?&lt;/p&gt;

&lt;p&gt;The trick is that &lt;code&gt;await&lt;/code&gt; returns, it doesn't pause at all, that is a misnomer. It does save the state of the function it's in first and registers a callback to that state with a promise, but after that it returns. That is the &lt;code&gt;await&lt;/code&gt; statement is a fancy variant of the &lt;code&gt;return&lt;/code&gt; statement. When you execute &lt;code&gt;await&lt;/code&gt; you are practically executing a dressed variant of &lt;code&gt;return&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The JavaScript continues executing in the function that called the one that the &lt;code&gt;await&lt;/code&gt; was in.&lt;/p&gt;

&lt;p&gt;To make clear what is happening here, a firm rule exists in JavaScript, that a function that uses &lt;code&gt;await&lt;/code&gt; must be marked &lt;code&gt;async&lt;/code&gt;. In a nutshell this is illegal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myfunc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I'm done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;we are obliged to write it so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myfunc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I'm done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In no small part, this is intended to remind anyone who calls this function that it may not be finished when it returns ... &lt;/p&gt;

&lt;p&gt;Forsooth, guess what?  &lt;code&gt;function myfunc()&lt;/code&gt; returns a string (or it would if it were legal and we took the &lt;code&gt;await&lt;/code&gt; out), but &lt;code&gt;async function myfunc()&lt;/code&gt; returns a promise for the string and that promise is still "pending" if &lt;code&gt;await&lt;/code&gt; is called, only when &lt;code&gt;myfunc()&lt;/code&gt; returns "I'm done" is the promise marked "fulfilled" (and and registered call backs are called).&lt;/p&gt;

&lt;p&gt;And so if you're content with a promise you can call &lt;code&gt;myfunc()&lt;/code&gt; and it will return a promise. But if you need a string, you can call &lt;code&gt;await myfunc()&lt;/code&gt; and you can see in this manner that &lt;code&gt;await&lt;/code&gt; begets &lt;code&gt;await&lt;/code&gt; and &lt;code&gt;async&lt;/code&gt; is a bit catchy, like a cold ... once a function uses &lt;code&gt;await&lt;/code&gt; and hence must be &lt;code&gt;async&lt;/code&gt;, slowly most functions that rely on it become &lt;code&gt;async&lt;/code&gt; as well, if they need results, otherwise a chain of promises is returned which is is fine too, if all you need is a promise. He who &lt;code&gt;awaits&lt;/code&gt; gets the results of the promise!&lt;/p&gt;

&lt;p&gt;But we were wondering how &lt;code&gt;await&lt;/code&gt; pauses. And it should be clear that it doesn't nor does it have to, JavaScript will still run to completion even though it is seemingly paused. &lt;/p&gt;

&lt;p&gt;It is not paused, it has saved its state, registered a callback with a Promise (just an object on the heap), which the browser has access to as well. But in so doing it returns to its calling function. That function can either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;also &lt;code&gt;await&lt;/code&gt; this function, in which case the same applies (recursively up all the calling functions until eventually JavaScript runs to completion, the awaits all having returned).&lt;/li&gt;
&lt;li&gt;not &lt;code&gt;await&lt;/code&gt;, be a normal function, which just runs to completion. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Then again ...
&lt;/h2&gt;

&lt;p&gt;Rather than &lt;code&gt;await&lt;/code&gt; it is often useful to register explicit callbacks with &lt;code&gt;.then()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the above example we could &lt;code&gt;await myfunc()&lt;/code&gt; or &lt;code&gt;myfunc().then(call_this_function)&lt;/code&gt;. Both register a call back with the promise. The &lt;code&gt;await&lt;/code&gt; registers a call back to the same function (state preserved). The &lt;code&gt;.then()&lt;/code&gt; registers a call back to &lt;code&gt;call_this_function&lt;/code&gt; (any function we name). &lt;/p&gt;

&lt;p&gt;Either way, both return and JavaScript runs to completion. it is when a browser event calls &lt;code&gt;resolve()&lt;/code&gt; (the first argument supplied to the promised function) that the promise's state is updated to "fulfilled" and the promise honours the call back requests registered with it (either back to the &lt;code&gt;await&lt;/code&gt; statement or the function registered with &lt;code&gt;.then()&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;And &lt;code&gt;setTimeout()&lt;/code&gt; is the means by which we place a call to &lt;code&gt;resolve()&lt;/code&gt; on the browser's to-do list! &lt;/p&gt;

&lt;h1&gt;
  
  
  Wrapping up
&lt;/h1&gt;

&lt;p&gt;But back to our cause which is deferring to the UI. We have a solution and we know how it works now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;defer_to_UI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;how_long&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;how_long&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in our time-consuming (UI locking) style in-lining function, from time to time we can &lt;code&gt;await defer_to_UI()&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The function of the &lt;code&gt;await&lt;/code&gt; being only to save the state of our function, register a desire to be called back to this same line, and return. So when we are called back we continue on the next line like nothing happened ... except of course that we released control (returned) and offered JavaScript the chance to process tasks on the event queue (the UI responds).&lt;/p&gt;

&lt;p&gt;From time to time, but ...&lt;/p&gt;

&lt;h1&gt;
  
  
  How often?
&lt;/h1&gt;

&lt;p&gt;If we look at my most extreme example of about 100,000 elements with styles to in-line taking about 30 seconds to do, if we defer to the UI after each element is processed it takes about 11 minutes to complete! Wow! And that is with no UI interactions. That, apparently, is the mere overhead of re-queuing ourselves 100,000 times. &lt;/p&gt;

&lt;p&gt;So clearly we don't want to do that. Instead we'd &lt;code&gt;defer_to_UI&lt;/code&gt; at some lower frequency, but what frequency? Either way, to get a feel for things, we should add a progress bar to the style in-lining function.&lt;/p&gt;

&lt;p&gt;Which we will look at in the next article ...&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>HTML Style In-lining Performance: And what to do about it</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Thu, 18 Nov 2021 07:47:46 +0000</pubDate>
      <link>https://forem.com/thumbone/html-style-in-lining-performance-and-what-to-about-it-4a55</link>
      <guid>https://forem.com/thumbone/html-style-in-lining-performance-and-what-to-about-it-4a55</guid>
      <description>&lt;p&gt;As part of the initially simply project of a &lt;a href="https://dev.to/thumbone/copy-to-clipboard-first-cut-1j5j"&gt;copy button on a web page&lt;/a&gt;, I discovered a need to in-line styles if they are to be conserved when pasted into an email, and that there are no satisfactory &lt;a href="https://dev.to/thumbone/in-lining-styles-client-side-9oi"&gt;out-of-the-box client-side JavaScript style in-lining libraries&lt;/a&gt; to be found. So I opt instead, for a native JavaScript solution.&lt;/p&gt;

&lt;p&gt;But it turns out that it's not quite so zippy in-lining styles like that. I'm not sure any other method is faster (except for Chrome's native method which on a Select and Copy in-lines them as good as instantly). &lt;/p&gt;

&lt;p&gt;A minor mystery, as we should be able to get performance as good as the browsers rendering (after all, the browser is interpreting the CSS on each element and working out where to place it and how to draw it, and our effort to simply work out a subset of that and inline it should by rights be as quick). But we don't seem to get native browser performance (one might suppose the native C or C++ browser implementation has an advantage of the interpreted Javascript implementation I guess). &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiditjn8jn6y2mcsz3w5n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiditjn8jn6y2mcsz3w5n.png" alt="Image description" width="773" height="223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Profiling it reveals no major culprits, it's all just milliseconds here, milliseconds there and it's not &lt;em&gt;bad&lt;/em&gt; performance. On a fairly ordinary element that I'd like to copy (a table of tables) I have about 3,500 elements, and these have styles in-lined in about 1 second  As a delay after pressing the copy button and having the copy available on clipboard that's not intolerable, it's quite acceptable, if not impressive.&lt;/p&gt;

&lt;p&gt;But what if we want to copy a lot? Well on one of my largest datasets I'd ever likely copy, I have about 100,000 elements and these have styles in-lined in about &lt;strong&gt;30 seconds&lt;/strong&gt;. That is getting a little clumsy to say the least. It means that if you click the copy button and then paste somewhere, chances are it's not available yet it's still being in-lined and not on the clipboard yet!&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcw1vkj7n9imva8f7tt0r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcw1vkj7n9imva8f7tt0r.jpg" alt="30 seconds" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What better reason to look into a few Javascript tricks to streamline this and hone our skills, to make that experience a bit slicker? &lt;/p&gt;

&lt;p&gt;Key strategies for improving the &lt;a href="https://www.nngroup.com/articles/definition-user-experience/"&gt;UX&lt;/a&gt; on such large copies (that can take 30 seconds to prepare) follow:&lt;/p&gt;
&lt;h1&gt;
  
  
  Separation of Tasks
&lt;/h1&gt;

&lt;p&gt;My first thought is to separate the tasks, of in-lining styles and of copying to the clipboard. If we in-line the styles when the page loads, then a copy is available instantly more or less to place on the clipboard when the copy button is clicked.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs5gql5d3jswzs8mfd8ov.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs5gql5d3jswzs8mfd8ov.jpg" alt="Separation" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alas if we do that in the javascript of the page we run into a couple of small problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Javascript is infamously single-threaded and this freezes the UI for the 30 seconds it takes to inline those styles. Longer even if you're more ambitious than I am with your copying ... (i.e. copying an even larger HTML element)&lt;/li&gt;
&lt;li&gt;if the element in question is itself generated by Javascript (as mine are) or worse fetched via Javascript (&lt;a href="https://www.w3schools.com/xml/ajax_intro.asp"&gt;AJAX&lt;/a&gt;), then the styles in-lined are not even true and complete because the element hasn't been fully rendered while the in-lining is running.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So ideally we want to do the in-lining after the page is fully rendered and complete - that is, schedule it for later.&lt;/p&gt;
&lt;h1&gt;
  
  
  Scheduling of Tasks
&lt;/h1&gt;

&lt;p&gt;The most generic way to do that, to ensure it runs not only after the DOM is fully loaded but also after all the dynamically Javascript-rendered elements are all settled, we can schedule the in-lining to happen when the page's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/readyState"&gt;ready state&lt;/a&gt; is "complete". That's easily done by watching for changes in ready state using a custom handler attached to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/readystatechange_event"&gt;Document: readystatechange event&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxsh7odt8qv6yiz35upf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxsh7odt8qv6yiz35upf.jpg" alt="Scheduling" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But ... even when it's running at the end of rendering, it can lock the UI up for those 30 seconds, quite a problem on an interactive page that just won't respond and appear locked while that's going on. And so ideally we want to free up the UI somehow while in-lining these styles.&lt;/p&gt;
&lt;h1&gt;
  
  
  Deferring to the UI
&lt;/h1&gt;

&lt;p&gt;To free up Javascript to handle UI interactions while our script is running, is essential for good UX. That is our script must defer to the UI ... &lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2fkxd4l5x9nkr119xe0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl2fkxd4l5x9nkr119xe0.jpg" alt="The Deferral Game" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is in fact a fairly widely used and cited little JavaScript snippet that does just that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));}&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But I have nowhere found any lucid explanation of what is going on there how and why this works, and I'll explore that in the next post.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>In-lining Styles Client Side</title>
      <dc:creator>Bernd Wechner</dc:creator>
      <pubDate>Fri, 12 Nov 2021 07:05:29 +0000</pubDate>
      <link>https://forem.com/thumbone/in-lining-styles-client-side-9oi</link>
      <guid>https://forem.com/thumbone/in-lining-styles-client-side-9oi</guid>
      <description>&lt;p&gt;Wanting to &lt;a href="https://dev.to/thumbone/html-on-the-clipboard-oh-what-shall-we-copy-21g9#-raw-style-endraw-attributes-aka-inlining-style-the-hard-way-sweatdrops"&gt;in-line styles&lt;/a&gt;, the first port of call, not least given Chrome-based browsers seem to in-line styles when using &lt;a href="https://dev.to/thumbone/copy-to-clipboard-the-options-1a07#select-and-copy-thumbsdown"&gt;Select and Copy&lt;/a&gt;, is to find an existing solution. Alas that search yielded no fruit. &lt;/p&gt;

&lt;p&gt;Here are some options that a search on the web uncovers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Automattic/juice"&gt;Juice&lt;/a&gt;, &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/inline-css"&gt;inline-css&lt;/a&gt;, &lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/css-inliner"&gt;css-inliner&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But none of them work well here. Let's look at why ... (or if you don't care why not, just jump straight to the Native Javascript solution at the end).&lt;/p&gt;

&lt;h2&gt;
  
  
  Juice 👎
&lt;/h2&gt;

&lt;p&gt;Looks sort of nice but oh so complicated. So many options. On the up side they do clarify that it can and does run client-side, and provide a test site:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://automattic.github.io/juice/"&gt;https://automattic.github.io/juice/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And it in-lines &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tagged HTML wonderfully. Works well.&lt;/p&gt;

&lt;p&gt;Still, it's overkill, brings with it dependencies &lt;a href="https://github.com/cheeriojs/cheerio"&gt;cheerio&lt;/a&gt;, &lt;a href="https://github.com/brettstimmerman/mensch"&gt;mensch&lt;/a&gt; and &lt;a href="https://github.com/subtleGradient/slick"&gt;slick&lt;/a&gt; and a look into the code behind the on-line demo makes rapidly clear that Juice is parsing the style sheets and applying them in what looks to be, must be, a product of its age. It has over 1500 lines of Javascript and the &lt;a href="https://github.com/Automattic/juice"&gt;repo&lt;/a&gt; was created in 2011 - the state of Javascript was woeful then compared with today (though not as woeful as in &lt;a href="http://www.jiehuang.net/wordPress/uncategorized/ecmascript/"&gt;2001&lt;/a&gt; 😏).&lt;/p&gt;

&lt;p&gt;So we have to give Juice full credit and admiration for its age and endurance, and that it's still being maintained and is useful. But I'll respectfully pass it by as I'm looking for a minimalist, native Javascript solution as far as possible, using the state of Javascript in 2021 a whole decade later (which in IT terms is an eon).&lt;/p&gt;

&lt;h2&gt;
  
  
  inline-css 👎
&lt;/h2&gt;

&lt;p&gt;Immediately disconcerting is the &lt;a href="https://www.npmjs.com/"&gt;npm&lt;/a&gt; documentation for this package. It says simply "Inspired by juice" and fails to tell us why the authors felt that embarking on a new project was worth their while. It hints at the fact that it's better than Juice in some way or other (why else start a new project inspired by it?), but lays no claim as to if or how that might be the case. A dark horse.&lt;/p&gt;

&lt;p&gt;The github repo dates to 2015 and so, it's not as long-in-the-tooth as Juice, but still, it likely leans on a more meager JavaScript standard than we have available today and quite possibly, like Juice resorts to parsing CSS code and interpreting it. It has about 1700 lines of Javascript and so is even larger than Juice! The last code contributions on &lt;a href="https://github.com/jonkemp/inline-css"&gt;github&lt;/a&gt; are early 2020 so at least it's being maintained which is good.&lt;/p&gt;

&lt;p&gt;Still, it also depends on &lt;a href="https://github.com/cheeriojs/cheerio"&gt;cheerio&lt;/a&gt;, and so falls a little short of a native solution but worse, it does not provide us with any suggestion as to run-context (server side or client side). &lt;/p&gt;

&lt;p&gt;It's on npm, which is &lt;a href="https://nodejs.org/"&gt;Node.js&lt;/a&gt; which suggests server-side JavaScript. It doesn't rule out that it  works client-side, but there is zero indication provided that it does nor a test site, the way Juice provided. And like it or not, I associate npm primarily with Node.js and server side applications (rightly or wrongly).&lt;/p&gt;

&lt;p&gt;So for my needs, it goes respectfully into the too-hard basket. Failed, in a sense, by the poor quality of its own introductory presentation. An irony, because there is some nice documentation there, just not a nice introduction that spells out whether it runs client-side or not, how to if so, and how it's different from Juice and why we'd use it. All the obvious questions I have surfing in on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  css-inliner 👎
&lt;/h2&gt;

&lt;p&gt;Long story short, the introductory documentation fails this package even more than that for inline-css did. Again, it's on npm, and so looks targeted at server-side use and makes no effort to mention the other two (Juice or inline-css), or differentiate itself from them. The &lt;a href="https://github.com/broadly/css-inliner"&gt;github repo&lt;/a&gt; dates back to 2015 as well and so it looks at some level to be an example of modern parallel development, this and inline-css emerging at the same time. And unsurprisingly, it again has around 1600 lines of JavaScript. The last code contribution was 2020 so again, at least  it's being maintained which is good I guess.&lt;/p&gt;

&lt;p&gt;Still, no hint as to the run-context it supports (server-side and/or client-side) but hints, via its npm references and templating languages that it's a server-side tool not a client-side tool. Which doesn't rule out client-side application but again no claim that it works in that context, and no tips on how (if it does) and no demo site, so it's a respectful pass. Still seeking a lean native Javascript solution in preference to some pet project lacking clarity and exhibiting bloat. &lt;/p&gt;

&lt;h3&gt;
  
  
  Native Javascript 👍
&lt;/h3&gt;

&lt;p&gt;The failure to find an existing solution, leads me to (seeking) a native JavaScript implementation and the journey of discovery that lies behind that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/styleSheets"&gt;&lt;code&gt;document.styleSheets&lt;/code&gt;&lt;/a&gt; provides us with a collection of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet"&gt;&lt;code&gt;CSSStyleSheets&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/rules"&gt;&lt;code&gt;CSSStyleSheet.rules&lt;/code&gt;&lt;/a&gt; in turn provides all of the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleRule"&gt;&lt;code&gt;CSSStyleRules&lt;/code&gt;&lt;/a&gt; in use and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/matches"&gt;&lt;code&gt;Element.matches()&lt;/code&gt;&lt;/a&gt; tells us if &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleRule/selectorText"&gt;&lt;code&gt;CSSStyleRule.selectorText&lt;/code&gt;&lt;/a&gt; applies to a given element. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle"&gt;&lt;code&gt;window.getComputedStyle()&lt;/code&gt;&lt;/a&gt; provides rapid access to the complete set of &lt;a href="http://xahlee.info/js/css_whats_computed_style.html"&gt;computed styles&lt;/a&gt;. That is, crucially it has taken all those styles that position things relative to the browser window and calculated where, in real coordinates it landed. Among other things. Crucially, it also has resolved all of CSS variables. It describes what an element actually looks like on the screen, rather than in the abstract CSS.&lt;/p&gt;

&lt;p&gt;We can use the style sheets and the computed styles to update the &lt;a href="https://www.w3schools.com/jsref/prop_html_style.asp"&gt;style&lt;/a&gt; attribute of each element and then, when all is done the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText"&gt;&lt;code&gt;innerText&lt;/code&gt;&lt;/a&gt; and the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML"&gt;&lt;code&gt;innerHTML&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/outerHTML"&gt;&lt;code&gt;outerHTML&lt;/code&gt;&lt;/a&gt; are available for the the &lt;code&gt;text/plain&lt;/code&gt; and &lt;code&gt;text/html&lt;/code&gt; MIME parts respectively.&lt;/p&gt;

&lt;p&gt;Given we're in-lining styles, and we don't want to actually change the element in the DOM (we're just aiming to copy it to the clipboard in one form or another), we have to take a copy of the DOM element and in-line the styles on that. We have to use the computed style from the source element however as it is rendered on screen and the copy is not (hence has not got computed styles). To wit, we will have a source and target element one that is in the DOM and provides computed styles, and one that is not in the DOM and receives in-lined style attributes.&lt;/p&gt;

&lt;p&gt;Importantly an implementation like this is maybe 200, or 300 lines of JavaScript and not 1500.&lt;/p&gt;

&lt;p&gt;That more or less covers us and we have a terse native JavaScript in-lining solution! &lt;/p&gt;

&lt;p&gt;But ... performance, performance ... turns out that it's not slow, but it's not fast either, let's take a look at performance next, before we dive into an actual code implementation of the above schema - next week's article.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>style</category>
      <category>inline</category>
    </item>
  </channel>
</rss>
