<?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>DEV Community: Tommy Brunn</title>
    <description>The latest articles on DEV Community by Tommy Brunn (@nevon).</description>
    <link>https://dev.to/nevon</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%2F128651%2F2958e7a8-8ce6-4f10-a93d-b266d74ffe86.jpeg</url>
      <title>DEV Community: Tommy Brunn</title>
      <link>https://dev.to/nevon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nevon"/>
    <language>en</language>
    <item>
      <title>Is commercial support a viable way to open-source sustainability?</title>
      <dc:creator>Tommy Brunn</dc:creator>
      <pubDate>Tue, 06 Oct 2020 13:34:02 +0000</pubDate>
      <link>https://dev.to/nevon/supporting-open-source-1n3j</link>
      <guid>https://dev.to/nevon/supporting-open-source-1n3j</guid>
      <description>&lt;p&gt;In the middle of the COVID-19 pandemic, I quit my job as a lead engineer at a &lt;a href="https://info.instabox.io/se-en/"&gt;successful startup&lt;/a&gt; and dedicated myself to open-source. As a generally risk-aware person, this was certainly a move that made me uncomfortable, but coming off of a successful career at &lt;a href="https://www.klarna.com/us/"&gt;Europe's largest fintech unicorn&lt;/a&gt; I felt like I had the leeway to take some risks.&lt;/p&gt;

&lt;p&gt;For the past 3 or so years, I have been developing &lt;a href="https://kafka.js.org"&gt;KafkaJS&lt;/a&gt;, a dependency-free, pure Javascript Apache Kafka client for NodeJS. Over that time, I have seen it grow from a small project mostly internal to the company we were working at, to now powering hundreds of businesses and helping thousands of developers.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EvCrzOcU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/EjpCkEoXsAAumsB.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--fPfC2I6Q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/552931785076527104/iQxr0T6a_normal.png" alt="Tommy Brunn profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Tommy Brunn
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        @tommybrunn
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Really cool to see that KafkaJS, the project I've been working on with &lt;a href="https://twitter.com/tulios"&gt;@tulios&lt;/a&gt; for the past 3 years, has finally passed kafka-node to become the #1 most downloaded &lt;a href="https://twitter.com/hashtag/kafka"&gt;#kafka&lt;/a&gt; client for &lt;a href="https://twitter.com/hashtag/nodejs"&gt;#nodejs&lt;/a&gt; 🎉&lt;br&gt;&lt;br&gt;&lt;a href="https://t.co/OrqaRtPikv"&gt;kafka.js.org&lt;/a&gt; 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      11:09 AM - 06 Oct 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1313436225416634368" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1313436225416634368" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      1
      &lt;a href="https://twitter.com/intent/like?tweet_id=1313436225416634368" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
      2
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;h1&gt;
  
  
  Scaling open-source
&lt;/h1&gt;

&lt;p&gt;Like most open-source projects, one challenge that we always had was finding time to not only develop it further with &lt;a href="https://github.com/tulios/kafkajs/pull/332"&gt;new features&lt;/a&gt; or &lt;a href="https://github.com/tulios/kafkajs/pull/496"&gt;improving existing ones&lt;/a&gt; and handling &lt;a href="https://medium.com/collaborne-engineering/how-we-cut-kafka-data-transfer-costs-by-90-4beb5c464cdf"&gt;valuable community contributions&lt;/a&gt;, but also providing support to our growing community of users.&lt;/p&gt;

&lt;p&gt;At the time of writing, we have approximately 500 developers in &lt;a href="https://kafkajs-slackin.herokuapp.com/"&gt;our Slack community&lt;/a&gt; and there's no shortage of &lt;a href="https://github.com/tulios/kafkajs/issues"&gt;issues being opened on Github&lt;/a&gt; that require triage. Over the years, I've been in numerous Slack conversations and video calls with developers debugging whatever issues they are having. This has been enormously helpful in understanding where the rough edges are and where people tend to get tripped up when building NodeJS applications with Kafka. On a personal level, it has also been very satisfying to get to see how KafkaJS is helping power systems in everything from commodities trading to medtech to commercial retail.&lt;/p&gt;

&lt;p&gt;However, there are only so many hours in a week that I can spend. Although open-source clearly brings value to thousands of companies, it's rare for any of that value to find its way back to the people that create it, and KafkaJS is no exception in that regard. In order to further scale the project and keep a healthy community going, there needs to be a mechanism in place to fund maintenance and development.&lt;/p&gt;

&lt;p&gt;While &lt;a href="https://github.com/sponsors/Nevon"&gt;Github sponsors&lt;/a&gt; and other fundraising tools are laudable efforts, in reality they don't provide any meaningful income except to a rare few extremely widely used projects. Even hugely impactful developers like TJ Holowaychuk &lt;a href="https://github.com/sponsors/tj"&gt;are barely making anything&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commercial support
&lt;/h2&gt;

&lt;p&gt;A time-tested way of funding open-source development is providing commercial support. It allows businesses to accelerate development while simultaneously funding the development of the software they rely on - a win-win.&lt;/p&gt;

&lt;p&gt;While this is typically used to fund larger projects, I don't see any inherent reason why it couldn't work for a medium-sized project such as ours. There are hundreds of businesses using it that could surely benefit from working with one of the few people that know it inside and out - I know because I have already done it on a volunteer basis for years.&lt;/p&gt;

&lt;p&gt;So, given that I'm finding myself willing to take on some risk, I'm giving it a shot!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_FgCTdJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2s119xmu4l890duae0gw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_FgCTdJt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2s119xmu4l890duae0gw.png" alt="Screenshot of KafkaJS website offering commercial support"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Whether you are using KafkaJS or are thinking of leveraging Kafka in NodeJS, &lt;a href="https://kafka.js.org/#support"&gt;get in touch and save yourself time and effort by working with an expert&lt;/a&gt;. Whether you are just starting out and want to validate your architectural choices, or you are already established and want to solve some gnarly problems together, I'm sure that my experience building secure, performant and maintainable systems in NodeJS can be of help to you!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you aren't in need of support, but just want to support my work anyway, &lt;a href="https://github.com/sponsors/Nevon"&gt;any contribution via Github Sponsors&lt;/a&gt; is greatly appreciated.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>opensource</category>
      <category>javascript</category>
      <category>kafka</category>
    </item>
    <item>
      <title>No Dependency is the Best Dependency</title>
      <dc:creator>Tommy Brunn</dc:creator>
      <pubDate>Wed, 15 Jul 2020 15:10:26 +0000</pubDate>
      <link>https://dev.to/nevon/no-dependency-is-the-best-dependency-14ee</link>
      <guid>https://dev.to/nevon/no-dependency-is-the-best-dependency-14ee</guid>
      <description>&lt;p&gt;Over the past few years, I have been interviewing hundreds of software developers for various positions in tech companies. One question that tends to come up rather frequently is how you pick which package you should depend on. Given that NPM alone currently has around 1 400 000 public packages, chances are that whatever your problem, there are multiple packages that all claim to solve it - leaving you to figure out the best way forward.&lt;/p&gt;

&lt;p&gt;Let's dive into how seemingly most people claim to choose packages, then I will share my alternatives and why any of this matters at all.&lt;/p&gt;

&lt;h1&gt;
  
  
  Github ⭐️ stars and other vanity metrics
&lt;/h1&gt;

&lt;p&gt;Taking the expression "100 Billion Flies Can't Be Wrong" to heart, by far the most common answer I've gotten is that they simply look at the number of stars the repository has on Github.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--usIyHNbo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/52x9d8f9a9k0ejigq0in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--usIyHNbo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/52x9d8f9a9k0ejigq0in.png" alt="Summary of the archived leftpad Github repository showing 1100 stars"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stars, for those who are unaware, is the version control platform equivalent of a Facebook "like". Clicking the ⭐️ Star button on a repository adds it to your list of starred repository and potentially shares that activity with your followers.&lt;/p&gt;

&lt;p&gt;The theory goes that if other people have deemed this package worthy of a star, it must be good enough for you as well. The benefit of this approach is naturally that it takes very little effort to simply compare a few numbers and pick the highest.&lt;/p&gt;

&lt;p&gt;There are of course other similar metrics that people use. &lt;a href="https://www.npmtrends.com"&gt;Npm Trends&lt;/a&gt; primarily uses number of downloads to compare package popularity over time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmtrends.com/kafkajs-vs-kafka-node-vs-node-rdkafka"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RidzvHBi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ze8mtxijs0w8sxyi7buo.png" alt="Graph showing download trends for KafkaJS, kafka-node and node-rdkafka"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The downside is that these numbers really says nothing about the quality of this package, whether it is still actively maintained (who goes back and removes stars?) or if it has bugs and edge cases that might not affect the majority but could affect you.&lt;/p&gt;

&lt;p&gt;In short, these types of metrics aim to measure the &lt;strong&gt;popularity&lt;/strong&gt; of a package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blogs, articles and tweets 🐦
&lt;/h2&gt;

&lt;p&gt;Another common criteria that I have heard is to look on Twitter or to read blogs to see what other people are recommending.&lt;/p&gt;

&lt;p&gt;This is again a form of external validation of a package's &lt;strong&gt;popularity&lt;/strong&gt;, but this time rather than relying on the wisdom of the crowd, we are choosing to rely on "expert opinion".&lt;/p&gt;

&lt;p&gt;At a surface level, this may perhaps sound fine. We expect that these experts on the internet have done a proper evaluation so that we don't have to. I do the same when choosing which barbecue to buy - I don't try out every one of them, I look at reviews.&lt;/p&gt;

&lt;p&gt;The issue here is that as evidenced by this very article, anyone can put their opinion on the internet - there's no peer review to ensure that my methodology was sound or that I don't have a vested interest in any particular product.&lt;/p&gt;

&lt;p&gt;However, even when an endorsement is done in good faith and a thorough evaluation was done and the facts properly presented, that person simply does not know the context of your project. Just because something was a good fit for them, that doesn't necessarily mean that it's a good fit for you.&lt;/p&gt;

&lt;h1&gt;
  
  
  Activity metrics - the BPM of the maintainer
&lt;/h1&gt;

&lt;p&gt;The MBAs of the development world shun popularity metrics like downloads and stars, and instead looks at "activity metrics", such as number of pull requests and issues, number of commits over time, number of releases and such.&lt;/p&gt;

&lt;p&gt;At the time of writing, &lt;a href="https://openbase.io/"&gt;Openbase.io&lt;/a&gt; was just released to the public, which aims to use these metrics to compare how well-maintained different packages are. For example, &lt;a href="https://openbase.io/js/redis"&gt;it can tell you that &lt;code&gt;redis&lt;/code&gt; (the NodeJS client) has on average 1 commit every 6 days, that the average PR takes 20 days to close and that the average issue stays open for 8 hours&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These types of metrics all serve to measure &lt;strong&gt;activity&lt;/strong&gt;. No one wants to deal with a project where the maintainers are impossible to reach or where bugs stay open for years.&lt;/p&gt;

&lt;p&gt;However, without &lt;em&gt;context&lt;/em&gt;, these metrics are not terribly informative.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some packages are simply &lt;strong&gt;done&lt;/strong&gt; and need no further activity. Simple packages that perform their task well and don't need to be kept up to date with moving targets very rarely need any maintenance - so why would a lack of commits be a negative?&lt;/li&gt;
&lt;li&gt;Depending on the target audience of your package, the issues you receive will be of varying quality. Even the most dedicated maintainer will struggle to close issues that require extended back-and-forths with unresponsive reporters that don't provide repro cases or failing tests.&lt;/li&gt;
&lt;li&gt;A simple way for a package author to improve their metrics is to simply close issues as "wontfix" as soon as possible. Or to integrate services like Dependabot, which keeps your dependencies up to date by creating pull requests that are very easily merged.&lt;/li&gt;
&lt;li&gt;Constant change isn't necessarily a good thing. For complex projects, landing a significant change may require careful thought and discussion over many months. Rushing to release something that's not quite thought through may just introduce bugs or churn as public APIs change.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As an open-source maintainer, this sort of evaluation criteria, while not without its merits, also has a tinge of entitlement. The vast majority of small (and not so small) open source projects are maintained without compensation by a single author. Expecting that person to be on call and to resolve every issue promptly is an awful attitude that is shockingly common. Later we will come back to what approach we can take to mitigate this risk in a responsible way.&lt;/p&gt;

&lt;h1&gt;
  
  
  Determining quality with this one weird trick...
&lt;/h1&gt;

&lt;p&gt;All of the criteria we have looked at above -&lt;strong&gt;popularity&lt;/strong&gt; and &lt;strong&gt;activity&lt;/strong&gt;- have all been about looking at &lt;em&gt;abstractions&lt;/em&gt; or high-level metrics to indicate something else - &lt;strong&gt;quality&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Determining quality requires you to do something that seemingly very few people actually resort to doing - &lt;em&gt;reading code&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Depending on the size and complexity of the project, as well as your familiarity with the subject, you may or may not be able to judge the technical merit of every single piece of software that you interact with. For example, I may be able to make use of Tensorflow to detect faces in an image, but I couldn't without some serious time investment compare the quality of Tensorflow to other competing solutions.&lt;/p&gt;

&lt;p&gt;However, I think people overestimate how complex &lt;em&gt;most&lt;/em&gt; software really is. I bet your average developer could come to an informed decision on the quality of for example &lt;a href="https://github.com/expressjs/express"&gt;&lt;code&gt;express&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://github.com/brianc/node-postgres"&gt;&lt;code&gt;pg&lt;/code&gt;&lt;/a&gt; in just a few hours if they weren't so reluctant to even try.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Seriously, this is my secret.&lt;/strong&gt; Try it next time. Actually sit down and read through the code and understand at least at a high level what it's doing and why. If you don't understand why it's doing something, read up on the subject until you get a decent understanding. Pretty soon you will notice similarities and differences between projects, understand what tradeoffs they made and how easy it will be to evolve the project.&lt;/p&gt;

&lt;p&gt;Be aware, however, that you are looking at a snapshot, not necessarily trajectory. When I first read through &lt;a href="https://github.com/brianc/node-postgres"&gt;&lt;code&gt;pg&lt;/code&gt;&lt;/a&gt; I saw that the protocol code was intermingled within all the other code, and thought that this would make it really hard for the project to evolve. If there was a competing postgres driver at the time, I would at least have had a serious look at that as an alternative. However, nowadays the protocol &lt;a href="https://github.com/brianc/node-postgres/tree/master/packages/pg-protocol/src"&gt;has been refactored and separated out into a separate package&lt;/a&gt; and the remaining codebase looks a lot better. If I had relied only on the quality metric at the time, I would most likely have made a poor choice.&lt;/p&gt;

&lt;h1&gt;
  
  
  Picking a dependency, the hard way
&lt;/h1&gt;

&lt;p&gt;When you choose to take on a new dependency in a project, you are marrying that dependency until death do you part, or you go through a messy and expensive divorce. If the dependency is central enough to your project (ORM, server or DI framework), replacing it may be almost equivalent to a full rewrite. Especially if the project has not been very well structured to isolate different layers, as is often the case. Therefore, it makes sense to invest time in understanding your dependencies and learning about them before it's too late.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the dependency have a sustainable development model (sponsorships, part-/full-time developer, commercial value added services or components)?&lt;/li&gt;
&lt;li&gt;Do I feel confident that if the maintainer stops maintaining the dependency, I could maintain it myself?&lt;/li&gt;
&lt;li&gt;Does an entity with resources have a vested interest in the future of the dependency, and could I or my company be that entity?&lt;/li&gt;
&lt;li&gt;Does my use-case fit with the goals of the dependency?&lt;/li&gt;
&lt;li&gt;If there are significant bugs that affect me, do I feel confident that I could fix them myself?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above points are primarily concerned with &lt;strong&gt;sustainability&lt;/strong&gt;. As we saw from the &lt;code&gt;pg&lt;/code&gt; example, a project being sustainable and having a positive trajectory is more important in the long run than quality at any particular snapshot in time. Or in the words of John Ousterhout:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A little bit of slope makes up for a lot of y-intercept&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Promoting sustainability
&lt;/h2&gt;

&lt;p&gt;Sustainable projects don't happen without either corporate or community support. If a project provides value for you or your company, and you want that project to continue to be supported, you have to contribute with your support.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contribute to &lt;a href="https://opencollective.com/discover?show=open%20source"&gt;open-source projects on OpenCollective&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Become a Github Sponsor of projects you depend on (shill alert: &lt;a href="https://github.com/sponsors/Nevon"&gt;Sponsor my work on KafkaJS&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also contribute your time by providing issue triage and pull requests to address bugs, however, be aware that code contributions also take the maintainer's time, not just when reviewing your contribution, but also because they have to continue to maintain your code for the lifetime of the project. Contributing financially to allow the maintainers to spend more of their time on the project is in many cases more impactful for the long-term health of the project.&lt;/p&gt;

&lt;h1&gt;
  
  
  How about no dependency?
&lt;/h1&gt;

&lt;p&gt;So far we have learned that tying the fate of your project to a dependency is risky business, and mitigating that risk by careful evaluation not just of popularity and activity, but also of quality and sustainability, can be a lot of work. And even when you do put in the work, there is always the risk that you make the wrong choice anyway.&lt;/p&gt;

&lt;p&gt;With this in mind, &lt;strong&gt;I propose that we shift our mindset from "which dependencies should I choose" to "should I use a dependency"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One of the core design choices we made when building &lt;a href="https://kafka.js.org"&gt;KafkaJS&lt;/a&gt; was that we would have little to no runtime dependencies. We chose that because we knew that KafkaJS would be a core part of our users' applications, and as such any dependency that we take on, our users would also have to take on as a transitive dependency. We also knew that these applications would live for quite a long time and power important business processes, so if we chose to take on a dependency that would no longer be maintained, it would be up to us to make sure that our users would not be exposed to new security issues.&lt;/p&gt;

&lt;p&gt;As such, we adopted our "no dependencies" approach - with one small exception, &lt;a href="https://github.com/dcodeIO/long.js"&gt;&lt;code&gt;long.js&lt;/code&gt;&lt;/a&gt;. That exception we chose to allow for because the scope of the dependency was small, we knew that native 64-bit integers for NodeJS was on the horizon, at which point we could get rid of it, and we were confident that we could maintain the library or our own fork ourselves if need be.&lt;/p&gt;

&lt;p&gt;Shifting the mindset from "which dependency should I choose" to "should I use a dependency" meant that we don't need to worry about any of the issues listed above. The downside is of course that we have to maintain more code ourselves - but a lot of the time the code that we actually need is so much less than what dependencies provide. Since we know the context of our project, we can implement just the bit that we need, not cater for the needs of the many.&lt;/p&gt;

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

&lt;p&gt;In short, taking on a dependency has a bigger impact than most people tend to consider. If you are working on a long-term project with actual consequences in case of failure, this is a responsibility you should take seriously. Evaluate your options carefully and weigh the risk vs reward for each dependency.&lt;/p&gt;

&lt;p&gt;And ⭐️s don't matter.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover image from &lt;a href="https://research.swtch.com/deps"&gt;Russ Cox's excellent article on the same subject&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>npm</category>
      <category>node</category>
      <category>dependencies</category>
      <category>opinion</category>
    </item>
    <item>
      <title>(Sort of) fixing autofocus in iOS Safari</title>
      <dc:creator>Tommy Brunn</dc:creator>
      <pubDate>Tue, 22 Nov 2016 18:05:06 +0000</pubDate>
      <link>https://dev.to/nevon/sort-of-fixing-autofocus-in-ios-safari-4675</link>
      <guid>https://dev.to/nevon/sort-of-fixing-autofocus-in-ios-safari-4675</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This article was originally posted on Medium in 2016. It may be out of date at this point.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  (Sort of) Fixing autofocus in iOS Safari
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;One of my colleagues is transitioning to the front-end team that I used to be a part of. To prepare him mentally for his journey into front-end development, I’ve been sending him a newsletter I call Front-End Hack of the Day. I’m posting them to Medium now for the world to enjoy&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Imagine that you are building a form where you would like to help the user out by automatically focusing on the first input field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"foo@example.com"&lt;/span&gt; &lt;span class="na"&gt;autofocus&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You fire it up and try it out, and it works great. Ship it!&lt;/p&gt;

&lt;p&gt;Sometime later, someone comes to you and says that it’s not working in iOS Safari. Off you go to &lt;a href="http://caniuse.com/#search=autofocus"&gt;caniuse.com&lt;/a&gt; and see that it is indeed not supported in that browser. Oh well, no big deal, we can fix that with a little bit of Javascript.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;DOMContentLoaded&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&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="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&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="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autofocus&lt;/span&gt;&lt;span class="dl"&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="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focus&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 your great surprise, you discover that this is not working either!&lt;/p&gt;

&lt;p&gt;Turns out, Apple &lt;em&gt;really&lt;/em&gt; doesn’t want you to focus input fields that the user hasn’t tapped on. Not only is the autofocus attribute not supported, but you have in fact made the situation worse!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--X_Nqi2j1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A0N27dTsgnb3VTJzFsnBOIA.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--X_Nqi2j1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/1024/1%2A0N27dTsgnb3VTJzFsnBOIA.jpeg" alt=""&gt;&lt;/a&gt;What I feel like doing any time I’m developing for Safari&lt;/p&gt;

&lt;p&gt;See, even manually calling focus on the element won’t work until the user has interacted with the page. If the input is inside an iframe and you try to call focus before the user has interacted, the keyboard opens, the input does &lt;strong&gt;not&lt;/strong&gt; get focus, and typing on the keyboard does absolutely nothing. As an added bonus, if the viewport scrolled at all, the useless, blinking cursor will be displayed somewhere outside the input.&lt;/p&gt;

&lt;p&gt;I haven’t been able to find any official resource explaining this decision, but I have to assume that it’s because focusing a field pops up the keyboard, which can be annoying if you didn’t have any intention of filling out the field.&lt;/p&gt;

&lt;h4&gt;
  
  
  Faking focus
&lt;/h4&gt;

&lt;p&gt;We can’t fully emulate the autofocus behavior, but we can get pretty close.&lt;/p&gt;

&lt;p&gt;Focusing a field does three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set focus styles&lt;/li&gt;
&lt;li&gt;Scroll the page so the field is somewhere in the middle of the viewport&lt;/li&gt;
&lt;li&gt;Open the keyboard&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;3 is the only thing that Apple has something against, but the other two can be implemented rather easily. I’m going to show you a very specific example, but for your own sanity, I suggest you come up with ways to abstract over this so that you don’t need to worry about whether you’re really focusing the field or if you’re just faking it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out &lt;a href="https://github.com/klarna/ui"&gt;@klarna/ui on Github&lt;/a&gt; to see how we are doing it (&lt;a href="https://github.com/klarna/ui/blob/master/lib/features/programmaticFocus/index.js"&gt;programmaticFocus.js&lt;/a&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first part is simple, to set the focus styles, just add a class with the same styling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nc"&gt;.has-focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;black&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;Scrolling the input into view is surprisingly simple, thanks to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView"&gt;Element.scrollIntoView&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If we put it all together, we get something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;isIos&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="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="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/iPad|iPhone/i&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;hasInteracted&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="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;interacted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onTouchStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;interacted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onTouchStart&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="nx"&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;touchstart&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="s1"&gt;onTouchStart&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;interacted&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;FOCUS_TYPES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;REAL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;real&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;FAKE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fake&lt;/span&gt;&lt;span class="dl"&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;getFocusType&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="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasInteracted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isIos&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;FOCUS_TYPES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REAL&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FOCUS_TYPES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FAKE&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;focus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&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="k"&gt;switch&lt;/span&gt; &lt;span class="nx"&gt;getFocusType&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;FOCUS_TYPES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REAL&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;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;FOCUS_TYPES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FAKE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;has-focus&lt;/span&gt;&lt;span class="dl"&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;onBlur&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&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="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;has-focus&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onBlur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;blur&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onBlur&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scrollIntoView&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;DOMContentLoaded&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="o"&gt;=&amp;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;autofocusedInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&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="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&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="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autofocus&lt;/span&gt;&lt;span class="dl"&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;focus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;autofocusedInput&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;What we end up with is a field that looks like it has focus and that is centered in the viewport. The keyboard won’t pop up, but that’s as close as we can get.&lt;/p&gt;

&lt;p&gt;Hopefully this has been useful to you. The intention of these posts is not to show you some groundbreaking new front-end technique, but just to share some of the hacks my colleagues and I have had to implement over the years to deal with various browser quirks.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This was written mostly from memory, with some input from&lt;/em&gt; &lt;a href="https://medium.com/u/dc0f4cb7debe"&gt;&lt;em&gt;Xavier Via&lt;/em&gt;&lt;/a&gt;&lt;em&gt;, so there may be some inaccuracies. Please leave a comment if I missed something.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ios</category>
      <category>javascript</category>
      <category>hacks</category>
      <category>css</category>
    </item>
    <item>
      <title>Detecting autofilled fields in Javascript</title>
      <dc:creator>Tommy Brunn</dc:creator>
      <pubDate>Mon, 21 Nov 2016 11:46:47 +0000</pubDate>
      <link>https://dev.to/nevon/detecting-autofilled-fields-in-javascript-l46</link>
      <guid>https://dev.to/nevon/detecting-autofilled-fields-in-javascript-l46</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This article was originally posted on Medium in 2016. It may be out of date at this point.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the browser autofills a form field, it adds some styling to highlight which fields it has edited. In Chrome, as you can see above, it adds a lovely yellow background color.&lt;/p&gt;

&lt;p&gt;In principle, I think this is a good idea, as it shows the user which fields they should pay attention to in order to see if everything was correctly filled out. However, it will most likely clash with the rest of your sites’ styling, so it would be nice if we could have some more control over it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nd"&gt;:-webkit-autofill&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;Much&lt;/span&gt; &lt;span class="err"&gt;nicer&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;bisque&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 can add styles by using the vendor prefixed pseudo-class &lt;em&gt;-webkit-autofill&lt;/em&gt;, but if you need to run some Javascript when a field gets autofilled, it gets a lot trickier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F916%2F1%2AOACF7P1AqnHU4oKLLh8IHw.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%2Fcdn-images-1.medium.com%2Fmax%2F916%2F1%2AOACF7P1AqnHU4oKLLh8IHw.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://klarna.github.io/ui/" rel="noopener noreferrer"&gt;@klarna/ui&lt;/a&gt;, &lt;a href="https://klarna.com" rel="noopener noreferrer"&gt;Klarna&lt;/a&gt;’s open-source UI component toolkit, we use the floating label pattern. The label is initially displayed like a placeholder, and when you start typing it transitions to a small label at the top of the field.&lt;/p&gt;

&lt;p&gt;This is done by listening for a change event and adding the class &lt;em&gt;is-filled&lt;/em&gt; to the field, which then applies the appropriate zooming and positioning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F904%2F1%2AbNXnDCkiIBL0mspFeTpEBg.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%2Fcdn-images-1.medium.com%2Fmax%2F904%2F1%2AbNXnDCkiIBL0mspFeTpEBg.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, no event whatsoever is fired when a field is autofilled, so there’s no way to know when to add the class.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;And this is where the hack comes in…&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Change event through animation
&lt;/h4&gt;

&lt;p&gt;We can’t know when the field value changes through autofill, but that’s not the only thing that happens when it gets autofilled — some styles are also applied!&lt;/p&gt;

&lt;p&gt;Unfortunately, we can’t really listen for a style change, but we can listen for the start of an animation, and we can start an animation in response to autofilling using the previously mentioned &lt;em&gt;-webkit-autofill&lt;/em&gt; pseudo-class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;onAutoFillStart&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;from&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="nt"&gt;to&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="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;onAutoFillCancel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;from&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="nt"&gt;to&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="nt"&gt;input&lt;/span&gt;&lt;span class="nd"&gt;:-webkit-autofill&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;Expose&lt;/span&gt; &lt;span class="err"&gt;a&lt;/span&gt; &lt;span class="err"&gt;hook&lt;/span&gt; &lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;JavaScript&lt;/span&gt; &lt;span class="err"&gt;when&lt;/span&gt; &lt;span class="err"&gt;autofill&lt;/span&gt; &lt;span class="err"&gt;is&lt;/span&gt; &lt;span class="err"&gt;shown&lt;/span&gt;
    &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;JavaScript&lt;/span&gt; &lt;span class="err"&gt;can&lt;/span&gt; &lt;span class="err"&gt;capture&lt;/span&gt; &lt;span class="err"&gt;'animationstart'&lt;/span&gt; &lt;span class="err"&gt;events&lt;/span&gt;
    &lt;span class="nl"&gt;animation-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;onAutoFillStart&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;Make&lt;/span&gt; &lt;span class="err"&gt;the&lt;/span&gt; &lt;span class="err"&gt;background&lt;/span&gt; &lt;span class="err"&gt;color&lt;/span&gt; &lt;span class="err"&gt;become&lt;/span&gt; &lt;span class="err"&gt;yellow&lt;/span&gt; &lt;span class="err"&gt;really&lt;/span&gt; &lt;span class="err"&gt;slowly&lt;/span&gt;
    &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;background-color&lt;/span&gt; &lt;span class="m"&gt;50000s&lt;/span&gt; &lt;span class="n"&gt;ease-in-out&lt;/span&gt; &lt;span class="m"&gt;0s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;:-webkit-autofill&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;Expose&lt;/span&gt; &lt;span class="err"&gt;a&lt;/span&gt; &lt;span class="err"&gt;hook&lt;/span&gt; &lt;span class="err"&gt;for&lt;/span&gt; &lt;span class="err"&gt;JS&lt;/span&gt; &lt;span class="err"&gt;onAutoFillCancel&lt;/span&gt;
    &lt;span class="err"&gt;//&lt;/span&gt; &lt;span class="err"&gt;JavaScript&lt;/span&gt; &lt;span class="err"&gt;can&lt;/span&gt; &lt;span class="err"&gt;capture&lt;/span&gt; &lt;span class="err"&gt;'animationstart'&lt;/span&gt; &lt;span class="err"&gt;events&lt;/span&gt;
    &lt;span class="nl"&gt;animation-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;onAutoFillCancel&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 what’s going on here, we are running the &lt;em&gt;onAutoFillStart&lt;/em&gt; animation when the psuedo class &lt;em&gt;-webkit-autofill&lt;/em&gt; is active, which it will be when Chrome autofills the input and changes the background color.&lt;/p&gt;

&lt;p&gt;Now we can listen for the start of that animation from within our 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;AUTOFILLED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is-autofilled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onAutoFillStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AUTOFILLED&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;onAutoFillCancel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AUTOFILLED&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;onAnimationStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;animationName&lt;/span&gt; &lt;span class="p"&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="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;animationName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onAutoFillStart&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="nf"&gt;onAutoFillStart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onAutoFillCancel&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="nf"&gt;onAutoFillCancel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input&lt;/span&gt;&lt;span class="dl"&gt;'&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;animationstart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onAnimationStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now whenever either the &lt;em&gt;onAutoFillStart&lt;/em&gt; or &lt;em&gt;onAutoFillCancel&lt;/em&gt; animations start playing, our corresponding functions will run, where we will add or remove the &lt;em&gt;is-autofilled&lt;/em&gt; class, or do whatever else we want to do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F914%2F1%2AwFilv4jvpGRB5SFngbiCFw.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%2Fcdn-images-1.medium.com%2Fmax%2F914%2F1%2AwFilv4jvpGRB5SFngbiCFw.png"&gt;&lt;/a&gt;Perfectly aligned labels with orange color to show that the field has been autofilled&lt;/p&gt;

&lt;p&gt;I hope this hack will be useful to you. If you want to take a peek at how this is used within &lt;a href="https://github.com/klarna/ui" rel="noopener noreferrer"&gt;@klarna/ui&lt;/a&gt;, &lt;a href="https://github.com/klarna/ui/tree/master/Field" rel="noopener noreferrer"&gt;you can find the implementation of the Field on Github.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>css</category>
    </item>
  </channel>
</rss>
