Acquia sponsors NPR

As I wrote in my previous post, you might be seeing a lot more of Acquia in the coming weeks. If you listen to NPR, you may have heard our new radio ads.

Like our highway billboards and train station takeover, our NPR campaign is another great opportunity to reach commuters.

NPR is a national non-profit media organization with a network of more than 1,000 affiliated radio stations across the United States — and quite a few use Drupal and Acquia for their sites. It boasts listenership of nearly 30 million, and its airwaves reach nearly 99 percent of Americans.

Our NPR ads are running during the morning and evening commutes. In addition, Acquia ads will be featured on the Marketplace Tech podcast, which is popular among technology decision makers. Between the podcasts and radio ads, the potential reach is 64 million impressions.

We have always believed in doing well by doing good. Sponsoring NPR creates brand awareness for Acquia, but also supports NPR financially. High-quality media organizations are facing incredible challenges today, and underwriting NPR's work is a nice way for Acquia to give back.


Source: Dries Buytaert www.buytaert.net


Watch Online: Tyrone vs Roscommon Live Free

Event type: 

User group meeting

What,,.,time,,.,is,,.,Tyrone,,.,v,,.,Roscommon?,,.,TV,,.,info,,,.,live,,.,stream,,,.,ticket,,.,info
Key,,.,information,,.,for,,.,Tyrone,,.,vs,,.,Roscommon,,.,in,,.,the,,.,Football,,.,Championship
All,,.,you,,.,need,,.,to,,.,know,,.,about,,.,Tyrone,,.,v,,.,Roscommon
STREAMING LINK>> https://freeallsportlive-tv.com/gaa
STREAMING LINK>> https://freeallsportlive-tv.com/gaa
Five,,.,things,,.,to,,.,know,,.,about,,.,Tyrone,,.,vs,,.,Roscommon,,,.,live,,.,on,,.,Sky,,.,Sports
Roscommon,,.,and,,.,Tyrone,,.,will,,.,go,,.,head,,.,to,,.,head,,.,on,,.,Saturday,,.,evening,,.,in,,.,Sky,,.,TV,,.,customers,,.,can,,.,also,,.,stream,,.,the,,.,match,,.,live,,.,on,,.,the,,.,Sky,,.,Go,,.,app.
LIVE/Sky-Sports*Tyrone,,.,vs,,.,Roscommon,,.,GAA,,.,Football,,.,Live,,.,Stream,,.,
[WATCH#TV]GAA**Tyrone,,.,vs,,.,Roscommon,,.,2018,,.,Live,,.,Stream,,.,GAA,,.,Football,,.,All-Ireland,,.,Senior,,.,Championship.
Tyrone,,.,v,,.,Roscommon,,.,Live,,.,Stream,,.,GAA,,.,Football,,.,All-Ireland,,.,Senior,,.,
Kevin,,.,McCabe,,.,(Tyrone),,.,Goal,,.,v,,.,Roscommon,,.,1992,,.,NFL,,.,Quarter-Fina
What,,.,time,,.,and,,.,channel,,.,is,,.,Tyrone,,.,vs,,.,Roscommon,,.,on?,,.,TV,,.,information,,.,
Tyrone,,.,and,,.,Roscommon,,.,clash,,.,in,,.,the,,.,Super,,.,8s,,.,this,,.,weekend.,,.,Share,,.,Sky,,.,TV,,.,customers,,.,also,,.,have,,.,the,,.,option,,.,to,,.,stream,,.,the,,.,match,,.,on,,.,the,,.,Sky,,.,Go,,.,app.,,.,You,,.,can,,.,also,,.,follow,,.,all,,.,the,,.,action,,.,on,,.,the,,.,Irish,,.,Mirror,,.,Sport’s,,.,live,,.,blog.
How,,.,to,,.,Watch***))Tyrone,,.,v,,.,Roscommon,,.,Live,,.,Stream,,.,GAA,,.,Football,,.,Five,,.,things,,.,to,,.,know,,.,about,,.,Tyrone,,.,vs,,.,Roscommon,,,.,live,,.,on,,.,Sky,,.,Sports
LIVE,,.,Tyrone,,.,vs,,.,Roscommon,,.,Live,,.,stream,,.,GAA,,.,Football,,.,All-Ireland,,.,
LivE-Tv@!!Tyrone,,.,vs,,.,Roscommon,,.,Live,,.,stream,,.,GAA,,.,2018,,.,free,,.,Tyrone,,.,vs,,.,Roscommon,,.,GAA,,.,live,,.,Stream,,.,Football,,.,All-Ireland,,.,Senior,,.,
LivE-Tv@!!Tyrone,,.,vs,,.,Roscommon,,.,Live,,.,stream,,.,GAA,,.,2018,,.,free,,.,-,,.,Watch,,.,
LivE-Tv@!!Tyrone,,.,vs,,.,Roscommon,,.,Live,,.,stream,,.,GAA,,.,2018,,.,free,,.,Tyrone,,.,vs,,.,Roscommon,,.,GAA,,.,live,,.,Stream,,.,Football,,.,All-Ireland,,.,Senior,,.,
How,,.,To,,.,Watch)##,,.,Tyrone,,.,vs,,.,Cork,,.,Live,,.,Stream,,.,TV,,.,Coverage,,.,Online,,.,All
Saturday,,.,Sport,,.,on,,.,RTÉ,,.,Radio,,.,1,,.,from,,.,2pm,,.,will,,.,have,,.,live,,.,commentary,,.,on,,.,Roscommon,,.,v,,.,Armagh,,,.,Tyrone,,.,vs,,.,Cork,,.,and,,.,Fermanagh,,.,v,,.,Kildare,,,.,
RTÉ,,.,™!~(Online)Tyrone,,.,vs,,.,Roscommon,,.,live,,.,Allianz,,.,Football,,.,League,,.,2018,,.,Watch,,.,Now,,.,Hosted,,.,by,,.,GAA,,.,Football,,.,All-Ireland,,.,Live,,.,Streaming.,,.,
Tyrone,,.,v,,.,Roscommon,,.,Live,,.,Stream,,.,GAA,,.,Football,,.,All-Ireland,,.,Senior,,.,
Tyrone,,.,v,,.,Roscommon,,.,Live,,.,Stream,,.,GAA,,.,Football,,.,All-Ireland,,.,Senior,,.,Championship,,.,Quarter,,.,Final,,.,2018″,,.,Live
GAAGO:,,.,Watch,,.,GAA,,.,Games,,.,(Live,,.,&,,.,On,,.,Demand),,.,on,,.,the,,.,official,,.,online,,.,
Live,,.,&,,.,Upcoming.,,.,catch,,.,up.,,.,{{,,.,match.date,,.,}},,,.,{{match.throw_in.substr(0,,,.,5)}},,.,(Irish,,.,time).,,.,Live,,.,now.,,.,Trailers.,,.,{{match,,.,Over,,.,100,,.,matches,,.,and,,.,programmes,,,.,live,,.,and,,.,later,,,.,in,,.,2018.,,.,$149,,.,Full,,.,studio,,.,coverage,,.,as,,.,broadcast,,.,to,,.,audiences,,.,in,,.,Ireland.
officialgaa,,.,Live,,.,Stream,,.,-,,.,GAA.ie
officialgaa,,.,Live,,.,Stream,,.,Cork,,.,v,,.,Tyrone,,.,Full-Time,,.,Tom,,.,Brewster,,.,Point,,.,-,,.,Armagh,,.,v,,.,GAANOW,,.,Rewind,,.,-,,.,2003,,.,Frankie,,.,Dolan,,.,point,,.,-,,.,Roscommon,,.,v,,.,Kildare
Sky,,.,Sports,,.,|,,.,Sports,,.,News
GAA,,.,team,,.,tracker,,.,LIVE!,,.,What,,.,to,,.,know,,.,about,,.,Tyrone,,.,vs,,.,Roscommon.,,.,Tyrone,,.,and,,.,Roscommon,,.,meet,,.,in,,.,the,,.,Super,,.,8s,,.,opener,,.,on,,.,Saturday,,,.,live,,.,and,,.,exclusive,,.,on,,.,
GAA,,.,-,,.,Watch,,.,Live,,.,GAA,,.,|,,.,TV,,.,Listings,,.,|,,.,Sky,,.,Sports
TV,,.,listings,,.,for,,.,all,,.,live,,.,GAA,,.,on,,.,Sky,,.,Sports,,.,-,,.,find,,.,out,,.,the,,.,dates,,,.,times,,.,and,,.,channels,,.,for,,.,the,,.,best,,.,netball,,.,coverage
GAA,,.,|,,.,Latest,,.,Gaelic,,.,Games,,.,News,,,.,Results,,.,&,,.,Fixtures,,.,|,,.,RTÉ,,.,Sport,,.,-,,.,RTE
All,,.,you,,.,need,,.,to,,.,know,,.,about,,.,Tyrone,,.,v,,.,Roscommon.,,.,10,,.,hrs.,,.,Kilkenny,,.,got,,.,the,,.,better,,.,of,,.,Limerick,,.,when,,.,the,,.,sides,,.,met,,.,in,,.,a,,.,qualifier,,.,in,,.,2017
Mayo,,.,vs,,.,Limerick,,.,live,,.,online,,.,-,,.,Home,,.,|,,.,Facebook
Mayo,,.,vs,,.,Limerick,,.,Live,,.,Stream,,.,GAA,,.,Football,,.,2018,,.,Online,,.,Free,,.,TV,,.,Football,,.,Championship,,.,first,,.,round,,.,qualifier,,.,draw,,.,made,,.,-,,.,Mayo,,.,face,,.,Limerick,,,.,Meath,,.,v,,.,Tyrone,,.,Watch,,.,Roscommon,,.,vs,,.,Leitrim,,.,Live,,.,Stream,,.,Online,,.,Connacht.
Galway,,.,v,,.,donegal,,.,live,,.,score,,.,-,,.,Eifelverein,,.,Untermosel
Football;,,.,Roscommon,,.,v,,.,Donegal,,,.,Dr,,.,Hyde,,.,Park,,.,Tyrone,,.,v,,.,Dublin,,,.,Watch,,.,Gaa,,.,live,,.,Stream,,.,Roscommon,,.,vs,,.,Antrim,,.,vs,,.,London,,.,vsTyrone,,.,vs
Watch!!,,.,Monaghan,,.,vs,,.,Derry,,.,Live,,.,—,,.,Steemit[WATCH-FREE],,.,Monaghan,,.,vs,,.,Roscommon,,.,GAA,,.,Live,,.,Stream,,.,2017,,.,.
Live,,.,GAA,,.,scores,,.,page,,.,-,,.,SportsNewsIRELAND
Live,,.,GAA,,.,scores,,,.,covering,,.,All-Ireland,,.,football,,.,and,,.,hurling,,.,scores,,.,Live,,.,score,,.,commentary,,.,updates,,.,from,,.,all,,.,levels,,.,of,,.,GAA,,,.,British,,.,and,,.,Irish,,.,Meath,,.,v,,.,Tyrone,,,.,Pairc,,.,Tailteann,,,.,5pm,,.,-,,.,SKY,,.,Roscommon,,.,v,,.,Sligo,,.,or,,.,Galway,,,.,Hyde,,.,Park,,,.,4pm,,.,-,,.,RTE.
ARTIST
tools driver TOOLS DRIVER
Source: https://groups.drupal.org/node/512931/feed


≛≛Watch*TV≛≛Serena Williams vs Angelique Kerber Live Stream Final ...

Start: 
2018-07-14 (All day) - 2018-07-16 (All day) UTC

Event type: 

User group meeting

≛≛Watch*TV≛≛Serena Williams vs Angelique Kerber Live Stream Final ...
●▬▬▬▬▬▬♪♫•¨•.¸¸ஜ۩❤۩ஜ¸¸.•¨•♫♪▬▬▬▬▬▬●
WATCH LIVE::: http://bit.ly/Williams-vs-Kerber-live-stream
WATCH LIVE::: http://bit.ly/Williams-vs-Kerber-live-stream
●▬▬▬▬▬▬♪♫•¨•.¸¸ஜ۩❤۩ஜ¸¸.•¨•♫♪▬▬▬▬▬▬●
Wimbledon 2018 Angelique Kerber vs Serena Williams Live Stream Free final When Angelique Kerber vs Serena Williams 2018 Wimbledon Women's final tennis prediction start time TV channel live streaming online and Wimbledon 2018 women's final When and where to watch Serena vs Kerber On HD TV.
Wimbledon 2018 Angelique Kerber vs Serena Williams final When and where to watch stream LIVE coverage. — By FPJ Web Desk Jul 14 .Wimbledon 2018 Angelique Kerber vs Serena Williams final When and where to watch stream LIVE coverage. — By FPJ Web Desk Jul 14 .
Sun Jul 15 Final Live Serena Williams vs Angelique Kerber 2018 Live On Free live serena williams vs angelique kerber live. Final Live Serena Williams Wimbledon 2018 Serena Williams and Angelique Kerber reach final live 2018 . wimbledon 2018 jelena ostapenko angeliq.
Serena Williams will face Angelique Kerber in a repeat of the 2016 final after both won . Live feed . Novak Djokovic vs Rafael Nadal#Wimbledon · July 12 2018 . I just feel I don't have anything to lose I can play so free.
Watch Angelique Kerber vs Serena Williams Live free Sphere Social
Angelique Kerber vs Serena Williams watch free live ☴Angelique Kerber vs . Australian Open 2018 When does it start what TV channel is it .VR Angelique Kerber vs Serena Williams Wimbledon Live Angelique Kerber vs Serena Williams 2018 Wimbledon Women's final .
Angelique Kerber vs Serena Williams watch live stream free ✥scores . Wimbledon 2018 Serena Williams and Angelique Kerber reach final .vr angelique kerber vs serena williams wimbledon live
Angelique Kerber vs Serena Williams 2018 Wimbledon Women's final tennis prediction start time TV channel live streaming online and Wimbledon 2018 women's final When and where to watch Serena wimbledon 2018 womens final when and where to watch.
Wimbledon 2018 women's final When and where to watch Serena Williams vs Angelique Kerber coverage on TV live streaming on Hotstar Wimbledon 2018 Serena Williams and Angelique Kerber triumph to Sport Tennis Wimbledon..
Wimbledon 2018 Serena Williams and Angelique Kerber triumph to set . Stream Type LIVE . Good morning and welcome to The Independent's live coverage of . Ostapenko's power and energy vs Kerber's experience and Watch live Wimbledon 2018 Serena Williams Angelique Kerber .
Watch live BBC coverage follow in play clips radio and text commentary from Wimbledon. . Catch up Wimbledon Serena Williams Angelique Kerber Jelena Ostapenko in women's . "I don't have anything to lose and I feel I can play so free. . Watch Bahrami Ivanisevic v Bates Castle court three.
Source: https://groups.drupal.org/node/512931/feed


[watch/"LIVE]~@@**" Serena Williams "vs" Angelique Kerber Live ...

Start: 
2018-07-14 (All day) - 2018-07-16 (All day) UTC

Event type: 

User group meeting

[watch/"LIVE]~@@**" Serena Williams "vs" Angelique Kerber Live ...
●▬▬▬▬▬▬♪♫•¨•.¸¸ஜ۩❤۩ஜ¸¸.•¨•♫♪▬▬▬▬▬▬●
WATCH LIVE::: http://bit.ly/Williams-vs-Kerber-live-stream
WATCH LIVE::: http://bit.ly/Williams-vs-Kerber-live-stream
●▬▬▬▬▬▬♪♫•¨•.¸¸ஜ۩❤۩ஜ¸¸.•¨•♫♪▬▬▬▬▬▬●
Wimbledon 2018 Angelique Kerber vs Serena Williams Live Stream Free final When Angelique Kerber vs Serena Williams 2018 Wimbledon Women's final tennis prediction start time TV channel live streaming online and Wimbledon 2018 women's final When and where to watch Serena vs Kerber On HD TV.
Wimbledon 2018 Angelique Kerber vs Serena Williams final When and where to watch stream LIVE coverage. — By FPJ Web Desk Jul 14 .Wimbledon 2018 Angelique Kerber vs Serena Williams final When and where to watch stream LIVE coverage. — By FPJ Web Desk Jul 14 .
Sun Jul 15 Final Live Serena Williams vs Angelique Kerber 2018 Live On Free live serena williams vs angelique kerber live. Final Live Serena Williams Wimbledon 2018 Serena Williams and Angelique Kerber reach final live 2018 . wimbledon 2018 jelena ostapenko angeliq.
Serena Williams will face Angelique Kerber in a repeat of the 2016 final after both won . Live feed . Novak Djokovic vs Rafael Nadal#Wimbledon · July 12 2018 . I just feel I don't have anything to lose I can play so free.
Watch Angelique Kerber vs Serena Williams Live free Sphere Social
Angelique Kerber vs Serena Williams watch free live ☴Angelique Kerber vs . Australian Open 2018 When does it start what TV channel is it .VR Angelique Kerber vs Serena Williams Wimbledon Live Angelique Kerber vs Serena Williams 2018 Wimbledon Women's final .
Angelique Kerber vs Serena Williams watch live stream free ✥scores . Wimbledon 2018 Serena Williams and Angelique Kerber reach final .vr angelique kerber vs serena williams wimbledon live
Angelique Kerber vs Serena Williams 2018 Wimbledon Women's final tennis prediction start time TV channel live streaming online and Wimbledon 2018 women's final When and where to watch Serena wimbledon 2018 womens final when and where to watch.
Wimbledon 2018 women's final When and where to watch Serena Williams vs Angelique Kerber coverage on TV live streaming on Hotstar Wimbledon 2018 Serena Williams and Angelique Kerber triumph to Sport Tennis Wimbledon..
Wimbledon 2018 Serena Williams and Angelique Kerber triumph to set . Stream Type LIVE . Good morning and welcome to The Independent's live coverage of . Ostapenko's power and energy vs Kerber's experience and Watch live Wimbledon 2018 Serena Williams Angelique Kerber .
Watch live BBC coverage follow in play clips radio and text commentary from Wimbledon. . Catch up Wimbledon Serena Williams Angelique Kerber Jelena Ostapenko in women's . "I don't have anything to lose and I feel I can play so free. . Watch Bahrami Ivanisevic v Bates Castle court three.
Source: https://groups.drupal.org/node/512931/feed


We have 10 days to save net neutrality

Last month, the Chairman of the Federal Communications Commission, Ajit Pai, released a draft order that would soften net neutrality regulations. He wants to overturn the restrictions that make paid prioritization, blocking or throttling of traffic unlawful. If approved, this order could drastically alter the way that people experience and access the web. Without net neutrality, Internet Service Providers could determine what sites you can or cannot see.

The proposed draft order is disheartening. Millions of Americans are trying to save net neutrality; the FCC has received over 5 million emails, 750,000 phone calls, and 2 million comments. Unfortunately this public outpouring has not altered the FCC's commitment to dismantling net neutrality.

The commission will vote on the order on December 14th. We have 10 days to save net neutrality.

Although I have written about net neutrality before, I want to explain the consequences and urgency of the FCC's upcoming vote.

What does Pai's draft order say?

Chairman Pai has long been an advocate for "light touch" net neutrality regulations, and claims that repealing net neutrality will allow "the federal government to stop micromanaging the Internet".

Specifically, Pai aims to scrap the protection that classifies ISPs as common carriers under Title II of the Communications Act of 1934. Radio and phone services are also protected under Title II, which prevents companies from charging unreasonable rates or restricting access to services that are critical to society. Pai wants to treat the internet differently, and proposes that the FCC should simply require ISPs "to be transparent about their practices". The responsibility of policing ISPs would also be transferred to the Federal Trade Commission. Instead of maintaining the FCC's clear-cut and rule-based approach, the FTC would practice case-by-case regulation. This shift could be problematic as a case-by-case approach could make the FTC a weak consumer watchdog.

The consequences of softening net neutrality regulations

At the end of the day, frail net neutrality regulations mean that ISPs are free to determine how users access websites, applications and other digital content.

It is clear that depending on ISPs to be "transparent" will not protect against implementing fast and slow lanes. Rolling back net neutrality regulations means that ISPs could charge website owners to make their website faster than others. This threatens the very idea of the open web, which guarantees an unfettered and decentralized platform to share and access information. Gravitating away from the open web could create inequity in how communities share and express ideas online, which would ultimately intensify the digital divide. This could also hurt startups as they now have to raise money to pay for ISP fees or fear being relegated to the "slow lane".

The way I see it, implementing "fast lanes" could alter the technological, economic and societal impact of the internet we know today. Unfortunately it seems that the chairman is prioritizing the interests of ISPs over the needs of consumers.

What can you can do today

Chairman Pai's draft order could dictate the future of the internet for years to come. In the end, net neutrality affects how people, including you and me, experience the web. I've dedicated both my spare time and my professional career to the open web because I believe the web has the power to change lives, educate people, create new economies, disrupt business models and make the world smaller in the best of ways. Keeping the web open means that these opportunities can be available to everyone.

If you're concerned about the future of net neutrality, please take action. Share your comments with the U.S. Congress and contact your representatives. Speak up about your concerns with your friends and colleagues. Organizations like The Battle for the Net help you contact your representatives — it only takes a minute!

Now is the time to stand up for net neutrality: we have 10 days and need everyone's help.
Source: Dries Buytaert www.buytaert.net


How the Roman Empire Made Pure CSS Connect 4 Possible

Experiments are a fun excuse to learn the latest tricks, think of new ideas, and push your limits. "Pure CSS" demos have been a thing for a while, but new opportunities open up as browsers and CSS itself evolves. CSS and HTML preprocessors also helped the scene move forward. Sometimes preprocessors are used for hardcoding every possible scenario, for example, long strings of :checked and adjacent sibling selectors.
In this article, I will walk through the key ideas of a Pure CSS Connect 4 game I built. I tried to avoid hardcoding as much as I could in my experiment and worked without preprocessors to focus on keeping the resulting code short. You can see all the code and the game right here:
See the Pen Pure CSS Connect 4 by Bence Szabó (@finnhvman) on CodePen.
Essential concepts
I think there are some concepts that are considered essential in the "pure CSS" genre. Typically form elements are used for managing state and capturing user actions. I was excited when I found people use <button type="reset"> to reset or start a new game. All you have to do is wrap your elements in a <form> tag and add the button. In my opinion this is a much cleaner solution than having to refresh the page.
My first step was to create a form element then throw a bunch of inputs into it for the slots and add the reset button. Here is a very basic demonstration of <button type="reset"> in action:
See the Pen Pure HTML Form Reset by Bence Szabó (@finnhvman) on CodePen.
I wanted to have nice visual for this demo to provide a full experience. Instead of pulling in an external image for the board or the discs, I used a radial-gradient(). A nice resource I often use is Lea Verou's CSS3 Patterns Gallery. It is a collection of patterns made by gradients, and they're editable too! I used currentcolor, which came pretty handy for the disc pattern. I added a header and reused my Pure CSS Ripple Button.
At this point the layout and disc design was already final, only the game didn't work at all
Dropping discs onto the board
Next I enabled users to take their turns dropping discs onto the Connect 4 board. In Connect 4, players (one red and one yellow) drop discs into columns in alternating turns. There are 7 columns and 6 rows (42 slots). Each slot can be empty or occupied by a red or yellow disc. So, a slot can have three states (empty, red, or yellow). Discs dropped in the same column are stacked onto each other.
I started out by placing two checkboxes for each slot. When they're both unchecked the slot is considered empty, and when one of them is checked the corresponding player has its disc in it.
The possible state of having them both checked should be avoided by hiding them once either of them is checked. These checkboxes are immediate siblings, so when the first of a pair is checked you can hide both by using :checked pseudo-class and the adjacent sibling combinator (+). What if the second is checked? You can hide the second one, but how to affect the first one? Well, there is no previous sibling selector, that's just not how CSS selectors work. I had to reject this idea.
Actually, a checkbox can have three states by itself, it can be in the indeterminate state. The problem is that you can't put it into indeterminate state with HTML alone. Even if you could, the next click on the checkbox would always make it transform into checked state. Forcing the second player to double-click when they make their move is unreliable and unacceptable.
I was stuck on the MDN doc of :indeterminate and noticed that radio inputs also have indeterminate state. Radio buttons with the same name are in this state when they're all unchecked. Wow, that's an actual initial state! What's really beneficial is that checking the latter sibling also has an effect on the former one! Thus I filled the board with 42 pairs of radio inputs.
In retrospect, clever ordering and usage of labels with either checkboxes or radio buttons would have made the trick, but I didn't consider labels to be an option to keep the code simpler and shorter.
I wanted to have large areas for interaction to have nice UX, so I thought it's reasonable to let players make a move by clicking on a column. I stacked controls of the same column on each other by adding absolute and relative positioning to the appropriate elements. This way only the lowest empty slot could be selected within a column. I meticulously set the time of transition of disc fall per row and their timing function is approximating a quadratic curve to resemble realistic free fall. So far the pieces of the puzzle came well together, though the animation below clearly shows that only the red player could make their moves.
Even though all the controls are there, only red discs can be dropped on the board
The clickable areas of radio inputs are visualized with colored but semi-transparent rectangles. The yellow and red inputs are stacked over each other six times(=six rows) per column, leaving the red input of the lowest row on top of the stack. The mixture of red and yellow creates the orangish color which can be seen on the board at start. The less empty slots are available in a column, the less intense this orangish color gets since the radio inputs are not displayed once they are not :indeterminate. Due to the red input always being precisely over the yellow input in every slot, only the red player is able to make moves.
Tracking turns
I only had a faint idea and a lot of hope that I can somehow solve switching turns between the two players with the general sibling selector. The concept was to let the red player take turn when the number of checked inputs was even (0, 2, 4, etc.) and let the yellow player take turn when that number was odd. Soon I realized that the general sibling selector does not (and should not!) work the way I wanted.
Then a very obvious choice was to experiment with the nth selectors. However attracting it was to use the even and odd keywords, I ran into a dead end. The :nth-child selector "counts" the children within a parent, regardless of type, class, pseudo-class, whatever. The :nth-of-type selector "counts" children of a type within a parent, regardless of class or pseudo-class. So the problem is that they cannot count based on the :checked state.
Well CSS counters count too, so why not give them a try? A common usage of counters is to number headings (even in multiple levels) in a document. They are controlled by CSS rules, can be arbitrarily reset at any point and their increment (or decrement!) values can be any integer. The counters are displayed by the counter() function in the content property.
The easiest step was to set up a counter and count the :checked inputs in the Connect 4 grid. There are only two difficulties with this approach. The first is you cannot perform arithmetics on a counter to detect if its is even or odd. The second is that you cannot apply CSS rules to elements based on the counter value.
I managed to overcome the first issue by making the counter binary. The value of the counter is initially zero. When the red player checks their radio button the counter is incremented by one. When the yellow player checks their radio button the counter is decremented by one, and so on. Therefore the counter value will be either zero or one, even or odd.
Solving the second problem required much more creativity (read: hack). As mentioned counters can be displayed, but only in the ::before and ::after pseudo-elements. That is a no-brainer, but how can they affect other elements? At the very least the counter value can change the width of the pseudo-element. Different numbers have different widths. Character 1 is typically thinner than 0, but that is something very hard to control. If the number of characters change rather than the character itself the resulting width change is more controllable. It is not uncommon to use Roman numerals with CSS counters. One and two represented in Roman numerals are the same character once and twice and so are their widths in pixels.
My idea was to attach the radio buttons of one player (yellow) to the left, and attach the radio buttons of the other player (red) to the right of their shared parent container. Initially, the red buttons are overlaid on the yellow buttons, then the width change of the container would cause the red buttons to "go away" and reveal the yellow buttons. A similar real-world concept is the sliding window with two panes, one pane is fixed (yellow buttons), the other is slidable (red buttons) over the other. The difference is that in the game only half of the window is visible.
So far, so good, but I still wasn't satisfied with font-size (and the other font properties) indirectly controlling the width. I thought letter-spacing would fit nicely here, since it only increases the size in one dimension. Unexpectedly, even one letter has letter spacing (which is rendered after the letter), and two letters render the letter spacing twice. Predictable widths are crucial to make this reliable. Zero width characters along with single and double letter spacing would work, but it is dangerous to set the font-size to zero. Defining large letter-spacing (in pixels) and tiny (1px) font-size made it almost consistent across all browsers, yes I'm talking about sub-pixels.
I needed the container width to alternate between initial size (=w) and at least double the initial size (>=2w) to be able to fully hide and show the yellow buttons. Let's say v is the rendered width of the 'i' character (lower roman representation, varies across browsers), and c is the rendered width (constant) of the letter-spacing. I needed v + c = w to be true but it couldn't be, because c and w are integers but v is non-integer. I ended up using min-width and max-width properties to constrain the possible width values, so I also changed the possible counter values to 'i' and 'iii' to make sure the text widths underflow and overflow the constraints. In equations this looked like v + c < w, 3v + 3c > 2w, and v << c, which gives 2/3w < c < w. The conclusion is that the letter-spacing has to be somewhat smaller than the initial width.
I have been reasoning so far as if the pseudo element displaying the counter value was the parent of the radio buttons, it is not. However, I noticed that the width of the pseudo-element changes the width of its parent element, and in this case the parent is the container of the radio buttons.
If you are thinking couldn't this be solved with Arabic numerals? You are right, alternating the counter value between something like '1' and '111' would also work. Nevertheless, Roman numerals gave me the idea in the first place, and they were also a good excuse for the clickbaity title so I kept them.
The players take alternating turns starting with the red player
Applying the technique discussed makes the parent container of the radio inputs double in width when a red input is checked and makes it original width when a yellow input is checked. In the original width container the red inputs are over the yellow ones, but in the double width container, the red inputs are moved away.
Recognizing patterns
In real life, the Connect 4 board does not tell you if you have won or lost, but providing proper feedback is part of good user experience in any software. The next objective is to detect whether a player has won the game. To win the game a player has to have four of their discs in a column, row or diagonal line. This is a very simple task to solve in many programming languages, but in pure CSS world, this is a huge challenge. Breaking it down to subtasks is the way to approach this systematically.
I used a flex container as the parent of the radio buttons and discs. A yellow radio button, a red radio button and a div for the disc belong to a slot. Such a slot is repeated 42 times and arranged in columns that wrap. Consequently, the slots in a column are adjacent, which makes recognizing four in a column the easiest part using the adjacent selector:
<div class="grid">
<input type="radio" name="slot11">
<input type="radio" name="slot11">
<div class="disc"></div>
<input type="radio" name="slot12">
<input type="radio" name="slot12">
<div class="disc"></div>
...
<input type="radio" name="slot16">
<input type="radio" name="slot16">
<div class="disc"></div>

<input type="radio" name="slot21">
<input type="radio" name="slot21">
<div class="disc"></div>
...
</div>
/* Red four in a column selector */
input:checked + .disc + input + input:checked + .disc + input + input:checked + .disc + input + input:checked ~ .outcome

/* Yellow four in a column selector */
input:checked + input + .disc + input:checked + input + .disc + input:checked + input + .disc + input:checked ~ .outcome
This is a simple but ugly solution. There are 11 type and class selectors chained together per player to cover the case of four in a column. Adding a div with class of .outcome after the elements of the slots makes it possible to conditionally display the outcome message. There is also a problem with falsely detecting four in a column where the column is wrapped, but let's just put this issue aside.
A similar approach for detecting four in a row would be truly a terrible idea. There would be 56 selectors chained together per player (if I did the math right), not to mention that they would have a similar flaw of false detection. This is a situation where the :nth-child(An+B [of S]) or the column combinators will come handy in the future.
For better semantics one could add a new div for each column and arrange the slot elements in them. This modification would also eliminate the possibility of false detection mentioned above. Then detecting four in a row could go like: select a column where the first red radio input is checked, and select the adjacent sibling column where the first red radio input is checked, and so on two more times. This sounds very cumbersome and would require the "parent" selector.
Selecting the parent is not feasible, but selecting the child is. How would detecting four in a row go with available combinators and selectors? Select a column, then select its first red radio input if checked, and select the adjacent column, then select its first red radio input if checked, and so on two more times. It still sounds cumbersome, yet possible. The trick is not only in the CSS but also in the HTML, the next column has to be the sibling of the radio buttons in the previous column creating a nested structure.
<div class="grid column">
<input type="radio" name="slot11">
<input type="radio" name="slot11">
<div class="disc"></div>
...
<input type="radio" name="slot16">
<input type="radio" name="slot16">
<div class="disc"></div>

<div class="column">
<input type="radio" name="slot21">
<input type="radio" name="slot21">
<div class="disc"></div>
...
<input type="radio" name="slot26">
<input type="radio" name="slot26">
<div class="disc"></div>

<div class="column">
...
</div>
</div>
</div>
/* Red four in a row selectors */
input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column > input:nth-of-type(2):checked ~ .column::after,
input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(4):checked ~ .column::after,
...
input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column > input:nth-of-type(12):checked ~ .column::after
Well the semantics are messed up and these selectors are only for the red player (another round goes for the yellow player), on the other hand it does work. A little benefit is that there will be no falsely detected columns or rows. The display mechanism of the outcome also had to be modified, using the ::after pseudo element of any matching column is a consistent solution when proper styling is applied. As a result of this, a fake eighth column has to be added after the last slot.
As seen in the code snippet above, specific positions within a column are matched to detect four in a row. The very same technique can be used for detecting four in a diagonal by adjusting these positions. Note that the diagonals can are in two directions.
input:nth-of-type(2):checked ~ .column > input:nth-of-type(4):checked ~ .column > input:nth-of-type(6):checked ~ .column > input:nth-of-type(8):checked ~ .column::after,
input:nth-of-type(4):checked ~ .column > input:nth-of-type(6):checked ~ .column > input:nth-of-type(8):checked ~ .column > input:nth-of-type(10):checked ~ .column::after,
...
input:nth-of-type(12):checked ~ .column > input:nth-of-type(10):checked ~ .column > input:nth-of-type(8):checked ~ .column > input:nth-of-type(6):checked ~ .column::after
The number of selectors have increased vastly in the final run, and this is definitely a place where CSS preprocessors could reduce the length of the declaration. Still, I think the demo is moderately short. It should be somewhere around the middle on the scale from hardcoding a selector for every possible winning pattern to using 4 magical selectors (column, row, two diagonals).
A message is shown when a player wins
Closing loopholes
Any software has edge cases and they need to be handled. The possible outcomes of a Connect 4 game are not only the red, or yellow player winning, but neither player winning filling the board known as draw. Technically this case doesn't break the game or produce any errors, what's missing is the feedback to the players.
The goal is to detect when there are 42 :checked radio buttons on the board. This also means that none of them are in the :indeterminate state. That is requiring a selection to be made for each radio group. Radio buttons are invalid, when they are :indeterminate, otherwise they are valid. So I added the required attribute for each input, then used the :valid pseudo-class on the form to detect draw.
The draw outcome message is shown when the board is filled
Covering the draw outcome introduced a bug. In the very rare case of the yellow player winning on last turn, both the win and draw messages are displayed. This is because the detection and display method of these outcomes are orthogonal. I worked around the issue by making sure that the win message has a white background and is over the draw message. I also had to delay the fade in transition of the draw message, so it would not get blended with the win message transition.
The yellow wins message is over the draw outcome preventing it to be displayed
While a lot of radio buttons are hid behind each other by absolute positioning, all of those in indeterminate state can still be accessed by tabbing through the controls. This enables players to drop theirs discs into arbitrary slots. A way to handle this is to simply forbid keyboard interactions by the tabindex attribute: setting it to -1 means that it should not be reachable via sequential keyboard navigation. I had to augment every radio input with this attribute to eliminate this loophole.
<input type="radio" name="slot11" tabindex="-1" required>
<input type="radio" name="slot11" tabindex="-1" required>
<div class="disc"></div>
...
Limitations
The most substantial drawback is that the board isn't responsive and it might malfunction on small viewports due to the unreliable solution of tracking turns. I didn't dare to take the risk of refactoring to a responsive solution, due to the nature of the implementation it feels much safer with hardcoded dimensions.
Another issue is the sticky hover on touch devices. Adding some interaction media queries to the right places is the easiest way to cure this, though it would eliminate the free fall animation.
One might think that the :indeterminate pseudo-class is already widely supported, and it is. The problem is that it is only partially supported in some browsers. Observe Note 1 in the compatibility table: MS IE and Edge do not support it on radio buttons. If you view the demo in those browsers your cursor will turn into the not-allowed cursor on the board, this is an unintentional but somewhat graceful degradation.
Not all browsers support :indeterminate on radio buttons
Conclusion
Thanks for making it to the last section! Let's see some numbers:

140 HTML elements
350 (reasonable) lines of CSS
0 JavaScript
0 external resources

Overall, I'm satisfied with the result and the feedback was great. I sure learned a lot making this demo and I hope I could share a lot writing this article!

How the Roman Empire Made Pure CSS Connect 4 Possible is a post from CSS-Tricks
Source: CssTricks


Techniques To Humanize Your Website and Connect With Your Audience

Have you ever visited a website and wondered if anyone was working round the clock behind the scenes to make sure that your user experience feels personal? A lot of business owners are using their websites as a way to connect with their prospective clients, especially if they cannot personally communicate with them. This is the reason you need to go to great lengths to humanize your website.
In this article, we will look into the importance of making your websites more human, and some effective tips and tricks to pull this off.

The website represents you and your business
The success of any business involves a lot of factors to consider. Some of these include having the right mindset on how to take care of the business, and having enough resources to start the business. Meanwhile, other companies focus on hiring the right people who can do the job, as well as providing products and services that people are likely to patronize.
All of these factors are key to making any business achieve its goals. But have you realized that you can accomplish all of these things by positioning your website properly?
The website lets people know more about your company
Years ago, promoting a business was done mainly through advertisements on TV, radio and print media. However, with the technology that we have today, the internet has become central to marketing – whether for brick-and-mortar stores or online businesses. These days, practically every business owner wants to take advantage of online media to promote its brand across the globe with just a few clicks.
Putting up a website has become one of the best marketing tools to reach out to consumers. Although earlier iterations webpages focused solely on content, functionality and user experience are now considered some of the key ingredients to make a website viral or popular.
Nevertheless, more than providing vivid colors and extraordinary web design, there are two important features that most consumers look for in a website: instantly available information, and customer satisfaction.
The website should give your prospective clients what they need
The content of the website plays a vital role in the success of any business. Website owners should stop beating around the bush and just go straight to the relevant information that a site visitor may want to have. Giving your site visitors high-value content propels them to browse through all of your site’s pages to check out what you can offer.
No matter what kind of content you provide, for as long as it is reader-friendly, then you can absolutely capture anyone’s attention.
Sure, you can configure your website to follow every detail in the SEO best practices rulebook. However, you need to remember that you are writing for humans – people who will potentially find your business on search engines. Do you think people would even bother to stay on your page for more than 5 seconds if they notice that you’re flooding them with repetitive keywords?
In other words, although using the right keywords in your site content is helpful in terms of SEO, you need to construct your website to satisfy your site visitors (and not the search bots).
Give your readers informative, interesting, and engaging material. Make sure that from the title, you already can excite their minds to read the rest of what is written on the page.

Tips to Humanize Your Website
One of the fundamental strategies to humanize your website is to make the content friendly and engaging to your intended audience. You want people to be interested in what you can offer them. Therefore, your goal is to create content that your target market likes.
It’s important for your site visitors to feel that they are being valued. They want to feel that their needs are being met by a real person, and not by a chatbot or a computer that’s running 24/7. In short, people want information with a heart.
Here are more techniques to humanize your website in order to connect with your target audience better:
Use human emotions

You’ve probably come across some websites where you feel like you’re reading your college professor’s lecture noes. That’s common among a lot of business websites, wherein they use a strictly formal tone by default.
While doing business is something to be taken seriously, not everyone may be interested to read technical jargon or overly serious content. That is why adding human emotions is effective – it can trigger a positive response among your visitors.
To achieve this, you may start listing down the products that your company offers, and identifying each of the product’s features that the people will probably appreciate. Be creative in using words that can trigger the user’s emotions and think about how it makes a user feel.
This can be successfully done by incorporating a story that everyone can relate to. People are fond of reading stories that they see themselves in. This kind of emotive or affective content (to humanize your website) will push them to action, whether it’s buying your products or signing up for your weekly newsletter.
Make use of creative content tools

It may be difficult to constantly update your website and provide new stories to tell your online visitors. The solution is simple: Search for apps that can help you create compelling headlines to keep your online visitors returning for more.
People generally like unique content along with high-quality images that can tickle their imagination as they read along. Once a visitor feels that the content is worth reading, he will likely become a regular follower of your website. This kind of following may even produce a lead or a sale.
Create unique content

How can you provide a weekly update without having the right materials to use? Get inspiration or ideas by checking what is trending. Discover what other people are talking about. and share something that is interesting and engaging.
Searching for trending hashtags can give you some ideas on how to create your content that will grab attention. Letting your followers realize that you’re updated with the latest trends is an effective way to humanize your website.
Find the right balance in terms of posting frequency

You may feel obliged to constantly update your content. But realistically, posting bi-monthly is ideal as you let your online viewers have time to read your latest post.
If you feel like sharing some more thoughts, you may do so by sharing shorter posts of about 500 words. Just make sure that everything that you share with your audience is carefully researched and written in high quality.
Initiate a call-to-action

Sometimes a website visitor may need a little more convincing before finally making a purchase. It can be helpful if you leave a question for readers to get them into thinking, or give them a direction (or call-to-action) to let them have an opportunity to decide.
For instance, you can offer them to sign up for a weekly update, or leave them a question to answer. This does not only make them feel that there is a real person behind the website, but it also helps in generating leads wherein they can be given priority in enjoying member-only promos.
Provide an opportunity for discussions

Some websites make use of a community board wherein visitors/consumers leave their comments, suggestions or opinions. This can help others in deciding to buy a certain product or service.
As a website owner, make sure that you take part in the discussions as well. By doing this, users are able to get first-hand information from you, in order to help them come up with a decision. In short, you can already humanize your website just by personally engaging with your followers.
Tap someone to check on your content

Having an amazing website should not only be based on visual aesthetics or effective social media sharing. There is also a need to make the content as perfect as possible in terms of grammar and spelling.
You may feel that you have made a well-written content, but it might be best to ask someone to review your work. This gives you better assurance that your text is 100% error-free.
At times, when we have a lot of things in our mind that we want to write about, we tend to think faster than we can write. As a result, we unknowingly miss out some punctuation marks, the plural forms of some words, misspelled words, and the like. Hiring a proofreader can help you create excellent content in which people can feel that you really know what you are talking about.
Besides, bad grammar may appear as if you used article spinning software to create your content. Aside from turning off your readers with weird text, article spinning also gets you penalized in terms of SEO.
Conclusion
Readers these days are very meticulous when it comes to browsing websites. Although web design continues to have importance in terms of catching attention, failing to humanize your website may turn off your potential clients.
The best way to connect with your prospects online is to make your website feel as human as possible. Nobody wants to experience talking to a robot while they’re browsing your site – unless if they’re huge fans of the Terminator’s Skynet!
Make your business flourish by making a website that can do more than merely enumerating your products or services. Humanize your website by telling a story to keep your online audience more engaged. People will become your loyal customers when you are able to strengthen your brand through sharing great content, providing high quality products and services, and establishing human communication that encourages interaction.
The post Techniques To Humanize Your Website and Connect With Your Audience appeared first on Web Designer Hub.
Source: http://www.webdesignerhub.com


Will Artificial Intelligence Replace Marketing Jobs?

Oh the promise of sophisticated analytics and artificial intelligence to automatically send current and potential customers the exact right email at the exact right time to get them to buy more things.I just perused a food delivery service this week and started the signup process before abandoning. The robots dutifully sent me follow-ups with coupons and welcome messages to convince me to become a customer.It’s no doubt effective when it’s done right. But I think it blinds most people trying to market their business to something even more important.Do you drink coffee? I bet you do. Afterall, 83% of the adult population does.It’s no wonder we do. Apart from being addictive it’s also convenient and it’s customized. Coffee historians call these the waves of coffee.Before 1850 people were grinding and roasting coffee themselves. The first wave was Folgers in a can; the second wave was Starbucks. You could now get coffee anyway you wanted and it was in itself a destination.So what’s the third wave of coffee? You go to a third wave coffee house like Stumptown or Intelligentsia, and there’s a good chance you can meet a coffee farmer walking around sampling their latest harvest. You can sign up for classes with your favorite barista. Come in once a week, and they’ll name your favorite drink after you.The third wave is all about making a deep and personal connection with your customers.The funny thing about these three waves is that once you understand them, you see them everywhere. Beer. Baking. Even carpentry. Watch any reality shows about a family building houses?One example that caught my eye recently was a young girl who had an early talent for music. Her family wanted to give her every chance to further her career, so they moved from Philadelphia to Nashville in 2003. Things didn’t take off immediately. But after a couple years, someone discovered our musician singing in a cafe and soon signed her up for her first record deal.She took off. She can sell out crowds instantly now.But the thing I’m most impressed about is her understanding of how music has evolved. Just like coffee. Music used to be hard to listen to. You’d have to expensively and inconveniently attend an event somewhere.Until the phonograph was invented. That was music’s first wave. Music became convenient. You could buy something and listen to it in your home.Radio was the second wave. Now you could have music everywhere and pick and choose the stations you wanted. Complete customization.But where’s the third wave?If you go to our young musicians YouTube channel, you can find a video of her celebrating Christmas. But it’s not with her family, it’s with her fans. She painstakingly wraps present after present. Her apartment is a complete mess of boxes, wrapping paper, and bubble wrap. And then she proceeds to not only ship these packages to her fans, but she delivers them herself, surprising people who thought they were waiting for some random UPS driver.Her fans are ecstatic. Everyone’s screaming and crying. You don’t have to be one of these young fans to have the heartwarming feelings roll over you too.This young musician epitomizes the third wave of music. We’ve evolved from convenience and customization to now wanting personal connections with the artists who matter to us. It’s not just about getting a signature. We want to talk to them on social media. Watch the behind the scenes of their lives. Get them to read our Tweets or comment on our Instas.My daughter was extra special the other day coming home from school and I wanted to surprise her with a treat. There’s a bakery we go by that has macarons, her favorite. But we passed the place up. Why?Because there’s another bakery a bit further away, whose owners, a mom and her talented baker of a daughter, have become friends of ours. They tell us about their lives and struggles, and we share ours.They didn’t have to blast me with email or laser targeted artificial intelligence campaigns. They just needed to be human. And it won our loyalty and repeat business.Our young musician hasn’t lost her touch with this even a few years after that Christmas video. Even just this past week, she had 500 fans over to her home in Rhode Island for private listening parties of her newest album. And this past week as I write this, Taylor Swift released her latest, Reputation.And truth be told, I still haven’t become a customer of that food delivery service despite the robo emails. But I own all of Taylor Swift’s work.P.S. You should follow me on YouTube: youtube.com/nathankontny where I share more about how we run our business, do product design, market ourselves, and just get through life. And if you need a zero-learning-curve system to track leads and manage follow-ups, try Highrise.(This article originally appeared on Quora)Will Artificial Intelligence Replace Marketing Jobs? was originally published in Signal v. Noise on Medium, where people are continuing the conversation by highlighting and responding to this story.


Source: 37signals


Hexatope

It was awesome to hear Charlotte Dann on CodePen Radio the other day, who is Kickstarting a new jewelry business. The idea is that you draw your own jewelry (everything you draw looks awesome because it's on this interesting hexagon grid) and then it gets actually made. This tying together of her passions sprang to life on CodePen.
Direct Link to Article — Permalink
Hexatope is a post from CSS-Tricks
Source: CssTricks


Writing style

Was there a source for my writing style or was it self discovered?body[data-twttr-rendered="true"] {background-color: transparent;}.twitter-tweet {margin: auto !important;}@natekontny as a follower of vlog, wanted to know, where did you pick up the style of writing from, was there any source, or self discovered — @seebiscutfunction notifyResize(height) {height = height ? height : document.documentElement.offsetHeight; var resized = false; if (window.donkey && donkey.resize) {donkey.resize(height); resized = true;}if (parent && parent._resizeIframe) {var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage) {window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize) {window.webkit.messageHandlers.resize.postMessage(height); resized = true;}return resized;}twttr.events.bind('rendered', function (event) {notifyResize();}); twttr.events.bind('resize', function (event) {notifyResize();});if (parent && parent._resizeIframe) {var maxWidth = parseInt(window.frameElement.getAttribute("width")); if ( 500 < maxWidth) {window.frameElement.setAttribute("width", "500");}}Surprise!Adapted from Gage SkidmoreMy dad loves talk radio. I remember as a kid driving around with him and the car tuned to WGN an AM station based here in Chicago. One of the personalities the station hosted was Paul Harvey. Paul Harvey had a popular segment called “The Rest of the Story”.I wasn’t in love with talk radio, but I enjoyed Paul. He always told some odd bits of someone’s story and concluded with the name of a fairly well known person he had just described.The surprise made the stories interesting.Murray Davis discovered this when he looked at what research papers spread more than others.An audience finds a proposition ‘interesting’ not because it tells them some truth they thought they already knew, but instead because it tells them some truth they thought they already knew was wrong.We love movies when the bad guy doesn’t hurt someone when we expect it. We devour books where the good guy unexpectedly turns out to be evil. We crave surprise.And so a big part of my writing style is simply trying to surprise people.Did you know that young kid who no one wanted to play on the radio turned out to be Justin Bieber? Or the two guys who struggled to become actors, so they decided to make their own movie instead, catapulting the careers of Matt Damon and Ben Affleck.On and on I’m looking for morsels of Paul Harvey/Murray Davis-like surprise. Is there something I can poke at that people currently assume? Or is there even something I can hold back through the course of the story that might surprise people at the end?“Meanwhile, back at the ranch”SourceI remember a Chemistry class I had in college. There was this Teaching Assistant (TA) who collected Beanie Babies and decided to sell his entire collection for a pretty good sum. He did it because he wanted to use his money to buy his girlfriend an engagement ring. This has nothing to do with anything, I just remember that being something cool the TA did.The TA was also the one to show me one of the first “viral videos” ever to hit the internet. It was a cartoon filmed with cutouts using stop motion animation of 4 kids swearing like crazy and Santa spinning Jesus over his head before throwing him across a field of snow.That was the second ever short episode of South Park that Trey Parker and Matt Stone created in 1995. Today, South Park is one of the most successful cartoons in history.I picked up a book once about writing. I can’t remember the name of it or the author of this chapter but it was all about an important technique the author used in their writing: weaving. Weave stories together. And that stuck with me.I started seeing it everywhere. You see it in authors like Malcolm Gladwell, Simon Sinek, The Heath Brothers, South Park.Wait, one of those isn’t like the other.South Park’s season 21 premier weaves stories about Amazon’s Alexa replacing people’s jobs and a fake TV show called White People Renovating Houses. Back and forth the show moves from this group of people arguing about Alexa taking over the world and the remodeling show. Until they converge.Some call this storytelling technique “Meanwhile, back at the ranch”, nodding to the days of the early silent cowboy films that needed to use subtitles to signal to people they were now literally going back to the ranch for the next thread of story.But you see it constantly in the shows and movies you love. One thread starts, and before it reaches a peak, the story moves you to another thread keeping you in suspense.The weave also helps in another form of surprise: showing you interesting contrasts between two things you might not have thought of being related before: Justin Bieber and ridiculous hard work, Stealing Cars and Frankenstein, Sansa Stark and building an audience.Don’t get caught stealingPhoto by Kelly Sikkema on UnsplashOne of my favorite vloggers right now is How to Dad. He’s got a bunch of funny videos showing exactly what his channel’s name describes: weird shit he goes through raising his kids.He’s been showing more videos recently of his daily life and you can see the things he’s “borrowing” from other vloggers. The selfie-sticks, the drone shots, the timelapses, the musical score. Except the musical score is now often him singing, playing the flute, or banging on his kids toys in his shower and recording the result. It’s a musical score unlike anything you’ve probably seen on a YouTube vlog.He’s taken pieces of things that have inspired him along the way and added his own bits of creativity to make it truly unique.Yes, I’ve been inspired by a great many people. And consciously and subconsciously I stand on their shoulders. But I make sure I’m only trying to take a piece of influence. I like his story structure. I like her use of surprise. I like how he uses narration in his videos. And I take all these pieces of things and merge them into a new whole adding my own unique bits.For example, probably unlike many of my favorite authors, I spend an inordinate amount of time paying attention to People Magazine, Vanity Fair, and Variety. One, I try like How to Dad, to add my own ingredients. And two, because I think there’s some great lessons from those channels people haven’t explored past their surface.Stop doing the same thing every single timeIf I came home on a weekend from college, I would go back to school late on a Sunday night because my mom and I couldn’t miss watching X-files together after dinner on Sunday.X-files was a fantastic show. Its peak season in my opinion was Season 5. That’s where they really stretched themselves creatively. They broke the usual formula of an episode and told stories through different main character perspectives, with different film making techniques, etc.That’s how all my favorite shows have worked. Sure they often have a go-to style, but they aren’t afraid to change it up constantly.And so there’s a style to my writing I reach for a lot, but I’m constantly trying new things and source material. Maybe tonight’s vlog episode is about the psychology of getting my daughter to swim and what that means for us as humans, or it’s simply a montage of the Highrise team enjoying our meetup.I can’t stand formulaic output over and over, and so I’m always looking for new styles and mediums to use.Do what you’re not passionate aboutPhoto by Lauren Peng on UnsplashAnd finally, my writing style is a product of me being interested in everything. I don’t know if it’s something I’ve been born with, or something I learned from my parents. I played every single sport growing up from Figure Skating to Football. I enjoy Justin Bieber and Phish. In college I took classes in Thermodynamics, Philosophy, Advanced Calculus, and Acupressure.I love variety.And that’s a big reason I can’t stand things like conferences in my industry. We’re all doing the same thing, and now we’re meeting to all talk about the same thing we’re all doing? Yuck :)My favorite conference/trade show I attended recently was a show in Food Technology. I didn’t have a direct use for any of the crazy robots and food packaging technology. But it was interesting seeing the trends in food product design and dissecting how they could be applied to other industries.Everyone is so obsessed with doing what they’re passionate about. Spend more time on things you start with zero interest in. Become interested in just being interested.So was there a source for my writing style or was it self discovered? Both. It was a lot of influence from people I enjoy and admire and also an attempt at being uniquely me.Put those two things together in everything you do and it’ll take you far in writing, work, and life.P.S. You should follow me on YouTube: youtube.com/nathankontny where I share more about how we run our business, do product design, market ourselves, and just get through life.And if you need a zero-learning-curve system to track leads and manage follow-ups you should try Highrise.Writing style was originally published in Signal v. Noise on Medium, where people are continuing the conversation by highlighting and responding to this story.


Source: 37signals


Breakthrough

A decade or so ago, a young musician couldn’t get anyone to play his music. He had raw talent, and just recorded his first album, but all the gatekeepers thought he sounded too young. Without Disney or Nickelodeon marketing his stuff, he was a dud.What does he do?I bet you know the names of a few famous impressionist painters. Monet. Manet. Degas. What makes them famous though? Are they really the best? Do you know a bad impressionist painter?What about Gustave Caillebotte?Caillebotte was an interesting impressionist. I don’t think anyone would say he’s bad, but he sure isn’t as popular as Monet.Caillebotte also has a quirky story. Upon his death he requested his art collection be hung in the Musée du Luxembourg in Paris. His art collection was about 70 paintings he had collected from his friends, also impressionists.They weren’t popular. They were actually the worst paintings of his friends. “Worst” being the ones his friends couldn’t get anyone else to buy. And at the time, people didn’t even like impressionism. Many hated it.So Caillebotte’s request in his will for the government to take his friends paintings and hang them in a museum was insane. How can someone force a museum to hang a bunch of paintings that no one liked or is even familiar with just because it’s a dead person’s request? It resulted in fierce criticism from the art world and public scrutiny.But Renoir finally convinced the museum to hang half of the collection 3 years after Caillebotte’s death. When the collection opened to the public, the museum was packed. Everyone wanted to see these paintings because they had generated so much scandal.Today, impressionism is mostly known for the work of the 7 greatest impressionist painters: Manet, Monet, Cézanne, Degas, Renoir, Pissarro, and Sisley.The 7 friends in Caillebotte’s collection.Sure Caillebotte had an eye for talent, and a belief impressionism would be admired at some point in the future.But what really happened is that the inadvertent exposure that Caillebotte brought to his friends also made people like them more.At least that’s the argument Derek Thompson makes in his book Hit Makers. Derek mentions James Cutting, a professor of psychology at Cornell University, and Cutting’s work to show how exposure begets likability.In Cutting’s experiments he had people compare famous paintings to more obscure works. Cutting proved the obvious — people prefered paintings from painters who are famous 6 out of 10 times.But when Cutting came up with an experiment to expose people to those obscure paintings 4 times more frequently than the famous paintings, people’s preferences switched. Now people preferred the more obscure paintings 8 out of 10 times.We don’t judge things just based on quality. Exposure changes our mind. The more we see, hear, or read something, the more we like it.That young musician had promise. But he needed to break through somehow. His manager came up with a plan. They were going to get in a van and travel around the country visiting every radio station he could. The kid is charming and has some talent, so it wasn’t as hard to schedule single visits to play an acoustic track from his record live on air.And this kid performed that track a lot. Eventually the exposure of playing the same song over and over again propelled “One Time” to the top of the charts and this musician is now a household name. This musician’s manager said:There’s not a DJ that can say they haven’t met Justin Bieber.There’s a lot to unpack from Justin’s rise to the sensation he is today. Not the least of which was the grit of a 14 year old kid who wouldn’t take no for an answer. Or the unwavering optimism he had of putting himself out there on YouTube uploading crappy videos of himself performing.But one of the most interesting aspects of Justin’s story is that to get through his obstacle, he went out and generated exposure to his work even if it wasn’t the exposure that he originally intended. He thought he could cut a record and get a ton of people listening to it. Instead he had to take the little wins and build from there.Most of us aren’t going to be the next Justin Beiber, but it’s still a lesson for us to go figure out how to get more exposure even if it isn’t the big splash we imagine we’re capable of.Want to be a headline speaker, go do talks at all the tiny chambers of commerce in front of 8 people for awhile. Want to get a byline in a famous publication, do hundreds of guest blog posts for whoever will pick you up.It’s a big reason I’ve generated the audience I have. I’m out there doing podcasts, daily vlog episodes, interviews, and writing articles in a ton of different places.Sometimes the opportunity is small. I’ll be the person’s first interview they’ve ever done. Doesn’t matter. Sometimes the message feels repetitive. I’ll be asked about the same question I’ve answered a million times. Doesn’t matter.I remind myself how often someone like a Justin Bieber played to just a handful of people at first or played the same single song over and over again without losing faith or enthusiasm. Or how Monet, no matter how talented he was, still needed the exposure, even if accidentally, a friend generated.Because in this day and age, even people with good products, talented musicians or painters, we all need to be out there generating as much exposure as possible to break through the noise.P.S. You should follow me on YouTube: youtube.com/nathankontny where I share more about how we run our business, do product design, market ourselves, and just get through life.And if you need a zero-learning-curve system to track leads and manage follow-ups you should try Highrise.Breakthrough was originally published in Signal v. Noise on Medium, where people are continuing the conversation by highlighting and responding to this story.


Source: 37signals


Revisiting our Fantasy Football Exploration

In 2015 Viget launched one of our most popular explorations around the future of fantasy football. As avid fans and players, we were interested in exploring the intersection of three of our biggest passions: technology, experience design, and sports. Our goal was to consider improvements to the fantasy football interfaces as well as concepts to push the fantasy experience beyond the screen. Through our research we specifically focused on designs for a more immersive draft experience, enhanced league communications, and better access to more statistical analysis. Two years has passed and the fantasy landscape is still booming with no signs of slowing down. As we start our 6th season of fantasy football at Viget we decided to dust off the ole exploration from the trophy shelf and see whether or not our ideas still hold water—or gatorade—or, well, you get the point.
Looking back, what began as a “wouldn’t it be great if…” exploration garnered positive and validating feedback asking if Viget was building out this experience as a new fantasy app. We were a bit overwhelmed with how many people really wanted to use this interface and the concepts we proposed. While the integrationof a new fantasy app was not necessarily (and is not) an immediate goal of our exploration, it did provide evidence that the 56 million fantasy players across the US and Canada care deeply about their fantasy experience and desire an experience that more closely matches their mature digital expectations.

A BRIEF HISTORY of FANTASY FOOTBALL
To understand the social expectations of fantasy players it’s important to understand the history of the fantasy experience. Fantasy sporting was born 55 years ago in Oakland from of a cocktail of sport fanaticism and statistical geekery. Drafts were completed in-person (often as full day events or parties), notepads and rulebooks were the recorders, and calculations each week were manually calculated by the league commissioner—the experience was as social as it was competitive. Yes, there was a strong analytical emphasis to the game, but it was surrounded by the social and emotional characteristics of a bowling league.

CREDIT: https://www.toyotahalloffame.com/history

The experience eventually found its way on the web in the mid-90s and broadened the appeal with new possibilities. No longer bound by geographic proximity, leagues could be formed with players across the country and drafts, calculations, and statistics could be automated in real time. Twenty years later, fantasy interfaces have adapted and evolved with new technologies, but the emphasis has mostly been on analytics and ubiquitousness.

Which brings us back to our fantasy exploration and considerations around what’s next for the fantasy experience. The debate on the best fantasy application is common water cooler fodder (and largely just a personal preference), but the feedback from our exploration did make us realize that users are quite savvy about digital. Ignoring the highly customizable experience sought by power users, most players really just want a fantasy experience that’s reflective of their everyday digital experiences.
More specifically, they’re seeking an experience full of social interactions, crowdsourced decision making, rich data visualizations, unique insights, and an experience that provides immediate enhancement of the live game through their virtual league. Surveying the industry again, we feel confident saying that our exploration and proposed features are still relevant in these areas. There’s still no single application that addresses all of these ideas, but we’re definitely seeing feature movement towards the sentiments we outlined in our work.
CURRENT TRENDS
Beyond this, we see three major areas of focus emerging in fantasy sports. These are marginally related to the broad themes we outlined in our exploration, and in many ways, reflective of the digital marketing industry as a whole. In no particular order, they are:
Diversified ContentSocial ExperiencesMicro/In-Game Fantasy

DIVERSIFIED CONTENT
The trends in fantasy content aren’t all that different than broader trends in content consumption. To say that “content is king” in fantasy would be an understatement. One could even argue that it is becoming one of the biggest differentiators in fantasy platforms (i.e. the provider’s ability to distribute and integrate unique fantasy content and analysis into their platform or channels). All of the major platforms are looking for every possible way to both create proprietary content and deliver it through their own unique mediums (devices, podcasts, user-generated, data-visualizations, etc.).
Consider the amount and range of content that ESPN is rolling out for the 2017 fantasy football season across all of their platforms—daily podcasts, 24 hour fantasy commentary, weekly recurring editorials, paid insider analysis, proprietary metrics and video analysis, tv programming, radio programming, expert chat access, and even fantasy conventions. This isn’t just a few tables of data to glance over each week and an article to read in your leisure. Players are consuming content from every angle and major content creators are providing it across every medium. 
 What was a fifteen minute commitment per week to set a line-up has turned into an all-consuming fantasy experience from every medium. Analysis and insights still reign supreme in weekly decision making and trading, but players don’t want to feel as if they are researching a term paper just to determine if player A or player B will lead them to victory. The experience itself needs to be more entertaining and distinct than academic, and people want it delivered within and outside of the platform. 
Besides your traditional stats, graphics, and editorial content, here are a few highlights of distinct content delivery in fantasy:

Alexa Hardware Integration Platforms like Yahoo! Fantasy have now added a fantasy football skill to Alexa so you can use voice commands to interact with your fantasy roster and get fantasy updates during live games. Additionally, content providers like Fantasy Football Nerd have added skills that analyze potential trades and provide roster recommendations and news, while stat-based providers like Let’s Talk Fantasy Football and Fantasy Matrix have added Alexa skills that give you voice-command access to any potential stat for any player since 2001. Want to find out how many targets Larry Fitzgerald averaged in Week 3 road games against a divisional rival? Just ask Alexa.

[youtube https://www.youtube.com/watch?v=jQ7EjnRJtUE]

In-App Game Video HighlightsIn one of the more intriguing content-related twists, the NFL.com will show their users, just-happened video highlights of NFL games as their fantasy players’ score. Not only will players get real-time notifications when their fantasy players earn them points, but they’ll be able to watch those scores happen (somewhat akin to having your personal DirectvRed Zone coverage of your fantasy game.) While it helps that they can leverage their ownership of this footage, it is certainly beginning to blur the line between fantasy and reality, and greater proof that investment in this kind of content experience is a key differentiator.

Fleaflicker ChartsWhile Fleaflicker is a smaller platform, they are investing in unique content like comparative player charts. Using historical and in-season data, Fleaflicker visualizes your players performance against both the average and best players at this position. Armed with this information, owners can make more informed decisions on starting or cutting their players without multiple clicks to make similar observations. 

SOCIAL INTEGRATION
As mentioned earlier, fantasy sports is as much about competition, statistical geekery, and glory as it is about socialization and camaraderie. Choosing a team name is as much a show of pop-culture urbanity and social commentary as it is a vehicle for insider jokes and references among of shared community of participants. Just peruse Reddit and you’ll find people discussing topics from best team names to draft strategies to people sharing their own custom calculated cheat sheets. Once the season begins, these discussions will change into projecting new players to pick up or using tools on 3rd-party content sites like Fantasy Pros to determine who you should start. Whatever the case may be, the social aspects of the fantasy experience are as critical to the experience as delivering quality content and analytics. Otherwise, would people go to these lengths for their draft day experiences?

Crowd-Sourced Decision MakingCrowd-sourced decision making is a staple in fantasy sports. Typically players have relied on 3rd-party sites for this informaiton, but the slightly newer platform offered by NFL.com has tapped into this behavior with their Fantasy Genius tool. Using the power of the crowd, their well-designed, dashboard-like interface presents thousands of user generated questions, answers, and polls through a variety of interactive components. All forms of prognostication are game while also tapping into the millions of hours of community knowledge in the process.

GIF Love Nothing says more with less than a well-timed gif. Yahoo! Fantasy knows this and recently introduced the ability to not only post these gems into an integrated chat application, but they’ve smartly provided keywords to the most common range of Sunday football watching emotions: winning, losing, gloating, crying, failing or just plain whatevering.

Your browser does not support the video tag.

Social Interaction FocusSleeperbot might be a newcomer to the fantasy world but they are putting great effort into maximizing social interactions within their app. They are aware that almost all of the fantasy experience is conducted on mobile and their choice to focus on the small screen experience distinguishes them from every competitor in the field. It’s well designed and modern with an integrated chat tool that is reminiscent of a fully featured application. Their approach to a mobile-first draftboard experience and new features like blockbuster trade support (to encourage more social interactions during the season) are steps towards a fantasy experience that encourages a range of game-day social interactions.

MICRO/IN-GAME FANTASY
In-game fantasy is not necessarily a trend that is featured in any major weekly fantasy platform up to this point, but there is movement towards more game-within-game, micro fantasy interactions. Fanamana has already built this technology/experience within baseball, and they are introducing a stand-alone experience for the NFL this fall. Additionally, start-ups like Pointstreak Sports Technologies are focused on using predictive contests to engage a younger, tech-savvy fan base as well as the more passive game watching fan. While this trend is more an extension of the overall growth of daily fantasy the implications or integration across all fantasy platforms has merit.
Short of interacting with their friends through social platforms and chat, the bulk of post-roster fantasy interactions are relatively passive. Players will check scores, watch highlights, and make a few minor roster tweaks, but largely they’re just consumers during the games. But what if additional, micro fantasy decisions could be introduced during this time? Additional fantasy decisions like in-game roster adjustments, predictions on player outcomes during specific moments in the game, or even predictions on outcomes within the fantasy match itself could result in additional points or prizes. This could potentially keep players even more actively engaged and keep them interacting with even more content throughout the entire fantasy experience.

WRAP-UP
When considering the current state and interest areas for many fantasy platforms, our fantasy football exploration from 2015 is still surprisingly relevant (in that most tech-related visions become quickly obsolete). The overall experience of fantasy still centers on intriguing content, rich data presentation, and social interactions. Unfortunately there hasn’t been much improvement in reimagining the bookends of the experience (the draft and post-game) but given the concentration on the trends above, there’s hope that they will be integrated into those aspects of the experience soon. We might have been a bit wide-eyed to dream that one application might tackle (pun intended) all of these aspects in a single platform, but we’ll take progress nonetheless.
We love fantasy sports at Viget and couldn’t be more excited about the upcoming season. That being said, we do hope the future continues to acknowledge that fantasy, is well...fantasy. Our dream is that new features and ideas enhance the physical and social experience through the virtual, but do so in a responsible manner. Focusing on digital improvements that augment the social roots of the game are of the utmost imperative. We’re proud to craft enjoyable, responsible, and engaging experiences at Viget whether they’re centered on fantasy or a very strict reality, and we hope our contribution in this area will have that type of impact.

Your browser does not support the video tag.


Source: VigetInspire


Corporate DrupalCoin Blockchain Developer - Seattle - Entercom Communications Corp. - Seattle, WA

Corporate DrupalCoin Blockchain Developer. As a DrupalCoin Blockchain Developer, you will work closely with developers, radio professionals, and other stakeholders to deploy new website...
From Entercom Communications Corp. - Sun, 03 Sep 2017 05:32:15 GMT - View all Seattle, WA jobs
Source: http://rss.indeed.com/rss?q=DrupalCoin Blockchain+Developer


Form Validation – Part 4: Validating the MailChimp Subscribe Form

Over the last few articles in this series, we've learned how to use a handful of input types and validation attributes to natively validate forms.
We've learned how to use the Constraint Validation API to enhance the native browser validation process for a better overall user experience. And we wrote a polyfill to extend support all the way back to IE9 (and plug a few feature holes in some newer versions).
Now, let's take what we've learned and apply it to a real example: the MailChimp signup form.

Article Series:

Constraint Validation in HTML
The Constraint Validation API in JavaScript
A Validity State API Polyfill
Validating the MailChimp Subscribe Form (You are here!)

A simple form with a large footprint
When you embed a MailChimp signup form on your site, it comes with a JavaScript validation script named `mc-validate.js`.
This file is 140kb (minified), and includes the entire jQuery library, two third-party plugins, and some custom MailChimp code. We can better!
See the Pen Form Validation: The MailChimp Standard Signup Form by Chris Ferdinandi (@cferdinandi) on CodePen.
Removing the bloat
First, let's grab a MailChimp form without any of the bloat.
In MailChimp, where you get the code for your embeddable form, click on the tab labelled "Naked." This version includes none of the MailChimp CSS or JavaScript.

<div id="mc_embed_signup">
<form action="//us1.list-manage.com/subscribe/post?u=12345abcdef&amp;id=abc123" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
<div id="mc_embed_signup_scroll">
<h2>Subscribe to our mailing list</h2>
<div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
<div class="mc-field-group">
<label for="mce-FNAME">First Name </label>
<input type="text" value="" name="FNAME" class="" id="mce-FNAME">
</div>
<div class="mc-field-group">
<label for="mce-EMAIL">Email Address <span class="asterisk">*</span></label>
<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
</div>
<div id="mce-responses" class="clear">
<div class="response" id="mce-error-response" style="display:none"></div>
<div class="response" id="mce-success-response" style="display:none"></div>
</div> <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_f2d244c0df42a0431bd08ddea_aeaa9dd034" tabindex="-1" value=""></div>
<div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
</div>
</form>
</div>
This is better, but it still includes some markup we don't need. Let's trim this down as much as possible.

We can remove the div#mc_embed_signup wrapper from around the form.
Similarly, we can remove the div#mc_embed_signup_scroll wrapper around the fields inside the form.
We can also remove the text informing visitors that "* indicates required."
Let's remove the .mc-field-group classes from around our form fields, and the empty class attributes on the fields themselves.
We should also remove the .required and .email classes from our email field, since they were only used as hooks for MailChimp validation script.
I went ahead and removed the * from the email label. It's totally up to you how you want to label required fields, though.
We can delete the div#mce-responses container, which is only used by the MailChimp JavaScript file.
We can also remove the .clear class from the div around the submit button.
Let's remove all of the empty value attributes.
Finally, we should remove the novalidate attribute from the form element. We'll let our script add that for us when it loads.

All of this leaves us with a much more clean and modest looking form. Since the MailChimp CSS is removed, it will inherit your site's default form styles.
<form action="//us1.list-manage.com/subscribe/post?u=12345abcdef&amp;id=abc123" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank">
<h2>Subscribe to our mailing list</h2>
<div>
<label for="mce-FNAME">First Name</label>
<input type="text" name="FNAME" id="mce-FNAME">
</div>
<div>
<label for="mce-EMAIL">Email Address</label>
<input type="email" name="EMAIL" id="mce-EMAIL">
</div>
<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_f2d244c0df42a0431bd08ddea_aeaa9dd034" tabindex="-1" value=""></div>
<div><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
</form>
See the Pen Form Validation: The MailChimp Simple Signup Form by Chris Ferdinandi (@cferdinandi) on CodePen.
Adding Constraint Validation
Now, let's add in a few input types and validation attributes so that the browser can natively validate the form for us.
The type for the email field is already set to email, which is great. Let's also add the required attribute, and a pattern to force emails to include a TLD (the .com part of an address). We should also include a title letting people know they have to have a TLD.
<form action="//us1.list-manage.com/subscribe/post?u=12345abcdef&amp;id=abc123" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank">
<h2>Subscribe to our mailing list</h2>
<div>
<label for="mce-FNAME">First Name</label>
<input type="text" name="FNAME" id="mce-FNAME">
</div>
<div>
<label for="mce-EMAIL">Email Address</label>
<input type="email" name="EMAIL" id="mce-EMAIL" title="The domain portion of the email address is invalid (the portion after the @)." pattern="^([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x22([^x0dx22x5cx80-xff]|x5c[x00-x7f])*x22)(x2e([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x22([^x0dx22x5cx80-xff]|x5c[x00-x7f])*x22))*x40([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x5b([^x0dx5b-x5dx80-xff]|x5c[x00-x7f])*x5d)(x2e([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x5b([^x0dx5b-x5dx80-xff]|x5c[x00-x7f])*x5d))*(.w{2,})+$" required>
</div>
<div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_f2d244c0df42a0431bd08ddea_aeaa9dd034" tabindex="-1" value=""></div>
<div><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
</form>
Enhancing with the Constraint Validation API
This is a great starting point, but we can enhance the user experience by adding the form validation script we wrote earlier in this series.
See the Pen Form Validation: MailChimp with the Constraint Validation API by Chris Ferdinandi (@cferdinandi) on CodePen.
Our validation script is just 6.7kb before minification, making it 20x smaller than the one MailChimp provides. If we want to ensure support back to IE9, though, we should include our Validity State polyfill and Eli Grey's classList.js polyfill.
See the Pen Form Validation: MailChimp with the API Script and Polyfills by Chris Ferdinandi (@cferdinandi) on CodePen.
That brings our total file size up to 15.5kb unminified—still 9× smaller than the MailChimp validation script.
Submitting the form with Ajax
The `mc-validate.js` script provided by MailChimp doesn't just validate the form. It also submits it with Ajax and displays a status message.
When you click submit on our modified form, it redirects the visitor to the MailChimp site. That's a totally valid way to do things.
But, we can also recreate MailChimp's Ajax form submission without jQuery for a better user experience.
The first thing we want to do is prevent the form from submitting via a page reload like it normally would. In our submit event listener, we're calling event.preventDefault if there are errors. Instead, let's call it no matter what.
// Check all fields on submit
document.addEventListener('submit', function (event) {

// Only run on forms flagged for validation
if (!event.target.classList.contains('validate')) return;

// Prevent form from submitting
event.preventDefault();

...

}, false);
See the Pen Form Validation: MailChimp and Prevent Default on Submit by Chris Ferdinandi (@cferdinandi) on CodePen.
Using JSONP
The mc-validate.js script uses JSONP to get around cross-domain security errors.
JSONP works by loading the returned data as a script element in the document, which then passes that data into a callback function that does all of the heavy lifting.
Setting up our Submit URL
First, let's set up a function we can run when our form is ready to be submitted, and call it in our submit event listener.
// Submit the form
var submitMailChimpForm = function (form) {
// Code goes here...
};

// Check all fields on submit
document.addEventListener('submit', function (event) {

...

// Otherwise, let the form submit normally
// You could also bolt in an Ajax form submit process here
submitMailChimpForm(event.target);

}, false);
The first thing we need to do is get the URL from the form's action attribute.
// Submit the form
var submitMailChimpForm = function (form) {

// Get the Submit URL
var url = form.getAttribute('action');

};
In the `mc-validate.js` script, the /post?u=' in the URL is replaced with /post-json?u=. We can do that quite easily with the replace() method.
// Submit the form
var submitMailChimpForm = function (form) {

// Get the Submit URL
var url = form.getAttribute('action');
url = url.replace('/post?u=', '/post-json?u=');

};
Serializing our form data
Next, we want to grab all of the form field data and create a query string of key/value pairs from it. For example, FNAME=Freddie%20Chimp&EMAIL=freddie@mailchimp.com.
Let's create another function to handle this for us.
// Serialize the form data into a query string
var serialize = function (form) {
// Code goes here...
};
Now, we want to loop through all of our form fields and create key/value pairs. I'll be building off of the work done by Simon Steinberger for this.
First, we'll create a serialized variable set as an empty string.
// Serialize the form data into a query string
// Forked and modified from https://stackoverflow.com/a/30153391/1293256
var serialize = function (form) {

// Setup our serialized data
var serialized = '';

};
Now let's grab all of the fields in our form using form.elements and loop through them.
If the field doesn't have a name, is a submit or button, is disabled, or a file or reset input, we'll skip it.
If it's not a checkbox or radio (a nice catchall for select, textarea, and the various input types) or it is and it's checked, we'll convert it to a key/value pair, add an & at the beginning, and append it to our serialized string. We'll also make sure to encode the key and value for use in a URL.
Finally, we'll return the serialized string.
// Serialize the form data into a query string
// Forked and modified from https://stackoverflow.com/a/30153391/1293256
var serialize = function (form) {

// Setup our serialized data
var serialized = '';

// Loop through each field in the form
for (i = 0; i < form.elements.length; i++) {

var field = form.elements[i];

// Don't serialize fields without a name, submits, buttons, file and reset inputs, and disabled fields
if (!field.name || field.disabled || field.type === 'file' || field.type === 'reset' || field.type === 'submit' || field.type === 'button') continue;

// Convert field data to a query string
if ((field.type !== 'checkbox' && field.type !== 'radio') || field.checked) {
serialized += '&' + encodeURIComponent(field.name) + "=" + encodeURIComponent(field.value);
}
}

return serialized;

};
See the Pen Form Validation: MailChimp with Ajax Submit - Serialized Form Data by Chris Ferdinandi (@cferdinandi) on CodePen.
Now that we have our serialized form data, we can add it to our URL.
// Submit the form
var submitMailChimpForm = function (form) {

// Get the Submit URL
var url = form.getAttribute('action');
url = url.replace('/post?u=', '/post-json?u=');
url += serialize(form);

};
Adding a callback
A key part of how JSONP works is the callback.
Traditional Ajax requests return data back to you. JSONP instead passes data into a callback function. This function has to be global (as in, attached to the window rather than inside of another function).
Let's create a callback function, and log the returned data in the console so that we can see what MailChimp sends back.
// Display the form status
var displayMailChimpStatus = function (data) {
console.log(data);
};
Now we can add this callback to our URL. Most JSONP use callback as the query string key for this, but MailChimp uses c.
// Submit the form
var submitMailChimpForm = function (form) {

// Get the Submit URL
var url = form.getAttribute('action');
url = url.replace('/post?u=', '/post-json?u=');
url += serialize(form) + '&c=displayMailChimpStatus';

};
Injecting our script into the DOM
Now we're ready to inject our script into the DOM. First, we'll create a new script element and assign our URL as it's src.
// Submit the form
var submitMailChimpForm = function (form) {

// Get the Submit URL
var url = form.getAttribute('action');
url = url.replace('/post?u=', '/post-json?u=');
url += serialize(form) + '&c=displayMailChimpStatus';

// Create script with url and callback (if specified)
var script = window.document.createElement( 'script' );
script.src = url;

};
Next, we'll grab the first <script> element we find in the DOM, and inject our new one just before it using the insertBefore() method.
// Submit the form
var submitMailChimpForm = function (form) {

// Get the Submit URL
var url = form.getAttribute('action');
url = url.replace('/post?u=', '/post-json?u=');
url += serialize(form) + '&c=displayMailChimpStatus';

// Create script with url and callback (if specified)
var script = window.document.createElement( 'script' );
script.src = url;

// Insert script tag into the DOM (append to <head>)
var ref = window.document.getElementsByTagName( 'script' )[ 0 ];
ref.parentNode.insertBefore( script, ref );

};
Finally, we'll remove it from the DOM after our script loads successfully.
// Submit the form
var submitMailChimpForm = function (form) {

// Get the Submit URL
var url = form.getAttribute('action');
url = url.replace('/post?u=', '/post-json?u=');
url += serialize(form) + '&c=displayMailChimpStatus';

// Create script with url and callback (if specified)
var script = window.document.createElement( 'script' );
script.src = url;

// Insert script tag into the DOM (append to <head>)
var ref = window.document.getElementsByTagName( 'script' )[ 0 ];
ref.parentNode.insertBefore( script, ref );

// After the script is loaded (and executed), remove it
script.onload = function () {
this.remove();
};

};
Processing the submit response
Right now, our callback method is just logging whatever MailChimp responds with into the console.
// Display the form status
var displayMailChimpStatus = function (data) {
console.log(data);
};
If you look at the returned data, it's a JSON object with two keys: result and msg. The result value is either error or success, and the msg value is a short string explaining the result.
{
msg: 'freddie@mailchimp.com is already subscribed to list Bananas Are Awesome. Click here to update your profile.'
result: 'error'
}

// Or...

{
msg: 'Almost finished... We need to confirm your email address. To complete the subscription process, please click the link in the email we just sent you.'
result: 'success'
}
See the Pen Form Validation: MailChimp with Ajax Submit - Result by Chris Ferdinandi (@cferdinandi) on CodePen.
We should check to make sure our returned data has both of these keys. Otherwise, we'll throw a JavaScript error when we go to use them.
// Display the form status
var displayMailChimpStatus = function (data) {

// Make sure the data is in the right format
if (!data.result || !data.msg ) return;

};
Display a status message
Let's add a <div> to our form, just before the submit button, that we'll use to add our error or success message. We'll give it a class of .mc-status.
<form action="//us1.list-manage.com/subscribe/post?u=12345abcdef&amp;id=abc123" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank">
/* ... */
<div class="mc-status"></div>
<div><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
</form>
In our displayMailChimpStatus() function, we want to find the .mc-status container and add our msg to it.
// Display the form status
var displayMailChimpStatus = function (data) {

// Get the status message content area
var mcStatus = document.querySelector('.mc-status');
if (!mcStatus) return;

// Update our status message
mcStatus.innerHTML = data.msg;

};
We can style the message differently depending on whether the submission was successful or not.
We already have some styles set up for our error messages with the .error-message, so let's reuse those. We'll create a new class, .success-message, for successful submissions.
.success-message {
color: green;
font-style: italic;
margin-bottom: 1em;
}
Now, we can conditionally add one of our classes (and remove the other) based on the result.
// Display the form status
var displayMailChimpStatus = function (data) {

// Get the status message content area
var mcStatus = document.querySelector('.mc-status');
if (!mcStatus) return;

// Update our status message
mcStatus.innerHTML = data.msg;

// If error, add error class
if (data.result === 'error') {
mcStatus.classList.remove('success-message');
mcStatus.classList.add('error-message');
return;
}

// Otherwise, add success class
mcStatus.classList.remove('error-message');
mcStatus.classList.add('success-message');

};
See the Pen Form Validation: MailChimp with Ajax Submit by Chris Ferdinandi (@cferdinandi) on CodePen.
An important accessibility improvement
While our message will be easily spotted by sighted users, people using assistive technology like screen readers may not inherently know a message has been added to the DOM.
We'll use JavaScript to bring our message into focus. In order to do so, we'll also need to add a tabindex of -1, as <div> elements are not naturally focusable.
// Display the form status
var displayMailChimpStatus = function (data) {

// Get the status message content area
var mcStatus = document.querySelector('.mc-status');
if (!mcStatus) return;

// Update our status message
mcStatus.innerHTML = data.msg;

// Bring our status message into focus
mcStatus.addAttribute('tabindex', '-1');
mcStatus.focus();

// If error, add error class
if (data.result === 'error') {
mcStatus.classList.remove('success-message');
mcStatus.classList.add('error-message');
return;
}

// Otherwise, add success class
mcStatus.classList.remove('error-message');
mcStatus.classList.add('success-message');

};
There's a good chance this will add a blue outline to our status message. This is a really important accessibility feature for links, buttons, and other naturally focusable content areas, but it's not necessary for our message. We can remove it with a little CSS.
.mc-status:focus {
outline: none;
}
The end result
We now have a lightweight, dependency-free script that validates our MailChimp form and submits it asynchronously.
Our completed script weighs 19kb unminified. When minified, the script weighs just 9kb. That's 15.5× smaller than the version MailChimp provides.
Not bad!

Article Series:

Constraint Validation in HTML
The Constraint Validation API in JavaScript
A Validity State API Polyfill
Validating the MailChimp Subscribe Form (You are here!)

Form Validation – Part 4: Validating the MailChimp Subscribe Form is a post from CSS-Tricks
Source: CssTricks


Form Validation Part 3: A Validity State API Polyfill

In the last article in this series, we built a lightweight script (6kb, 2.7kb minified) using the Validity State API to enhance the native form validation experience. It works in all modern browsers and provides support IE support back to IE10. But, there are some browser gotchas.
Not every browser supports every Validity State property. Internet Explorer is the main violator, though Edge does lack support for tooLong even though IE10+ support it. And Chrome, Firefox, and Safari got full support only recently.
Today, we'll write a lightweight polyfill that extends our browser support all the way back to IE9, and adds missing properties to partially supporting browsers, without modifying any of the core code in our script.

Article Series:

Constraint Validation in HTML
The Constraint Validation API in JavaScript
A Validity State API Polyfill (You are here!)
Validating the MailChimp Subscribe Form (Coming Soon!)

Let's get started.
Testing Support
The first thing we need to do is test the browser for Validity State support.
To do that, we'll use document.createElement('input') to create a form input, and then check to see if the validity property exists on that element.
// Make sure that ValidityState is supported
var supported = function () {
var input = document.createElement('input');
return ('validity' in input);
};
The supported() function will return true in supporting browsers, and false in unsupported ones.
It's not enough to just test for the validity property, though. We need to make sure the full range of Validity State properties exist as well.
Let's extend our supported() function to test for all of them.
// Make sure that ValidityState is supported in full (all features)
var supported = function () {
var input = document.createElement('input');
return ('validity' in input && 'badInput' in input.validity && 'patternMismatch' in input.validity && 'rangeOverflow' in input.validity && 'rangeUnderflow' in input.validity && 'stepMismatch' in input.validity && 'tooLong' in input.validity && 'tooShort' in input.validity && 'typeMismatch' in input.validity && 'valid' in input.validity && 'valueMissing' in input.validity);
};
Browsers like IE11 and Edge will fail this test, even though they support many Validity State properties.
Check input validity
Next, we'll write our own function to check the validity of a form field and return an object using the same structure as the Validity State API.
Setting up our checks
First, we'll set up our function and pass in the field as an argument.
// Generate the field validity object
var getValidityState = function (field) {
// Run our validity checks...
};
Next, let's setup some variables for a few things we're going to need to use repeatedly in our validity tests.
// Generate the field validity object
var getValidityState = function (field) {

// Variables
var type = field.getAttribute('type') || input.nodeName.toLowerCase(); // The field type
var isNum = type === 'number' || type === 'range'; // Is the field numeric
var length = field.value.length; // The field value length

};
Testing Validity
Now, we'll create the object that will contain all of our validity tests.
// Generate the field validity object
var getValidityState = function (field) {

// Variables
var type = field.getAttribute('type') || input.nodeName.toLowerCase();
var isNum = type === 'number' || type === 'range';
var length = field.value.length;

// Run validity checks
var checkValidity = {
badInput: false, // value does not conform to the pattern
rangeOverflow: false, // value of a number field is higher than the max attribute
rangeUnderflow: false, // value of a number field is lower than the min attribute
stepMismatch: false, // value of a number field does not conform to the stepattribute
tooLong: false, // the user has edited a too-long value in a field with maxlength
tooShort: false, // the user has edited a too-short value in a field with minlength
typeMismatch: false, // value of a email or URL field is not an email address or URL
valueMissing: false // required field without a value
};

};
You'll notice that the valid property is missing from the checkValidity object. We can only know what it is after we've run our other tests.
We'll loop through each one. If any of them are true, we'll set our valid state to false. Otherwise, we'll set it to true. Then, we'll return the entire checkValidity.
// Generate the field validity object
var getValidityState = function (field) {

// Variables
var type = field.getAttribute('type') || input.nodeName.toLowerCase();
var isNum = type === 'number' || type === 'range';
var length = field.value.length;

// Run validity checks
var checkValidity = {
badInput: false, // value does not conform to the pattern
rangeOverflow: false, // value of a number field is higher than the max attribute
rangeUnderflow: false, // value of a number field is lower than the min attribute
stepMismatch: false, // value of a number field does not conform to the stepattribute
tooLong: false, // the user has edited a too-long value in a field with maxlength
tooShort: false, // the user has edited a too-short value in a field with minlength
typeMismatch: false, // value of a email or URL field is not an email address or URL
valueMissing: false // required field without a value
};

// Check if any errors
var valid = true;
for (var key in checkValidity) {
if (checkValidity.hasOwnProperty(key)) {
// If there's an error, change valid value
if (checkValidity[key]) {
valid = false;
break;
}
}
}

// Add valid property to validity object
checkValidity.valid = valid;

// Return object
return checkValidity;

};
Writing the Tests
Now we need to write each of our tests. Most of these will involve using a regex pattern with the test() method against the field value.
badInput
For badInput, if the field is numeric, has at least one character, and at least one of the characters isn't a number, we'll return true.
badInput: (isNum && length > 0 && !/[-+]?[0-9]/.test(field.value))
patternMismatch
The patternMismatch property is one of the easier ones to test. This property is true if the field has a pattern attribute, has at least one character, and the field value doesn't match the included pattern regex.
patternMismatch: (field.hasAttribute('pattern') && length > 0 && new RegExp(field.getAttribute('pattern')).test(field.value) === false)
rangeOverflow
The rangeOverflow property should return true if the field has a max attribute, is a numeric, and has at least one character that's over the max value. We need to convert the string value of max to an integer using the parseInt() method.
rangeOverflow: (field.hasAttribute('max') && isNum && field.value > 1 && parseInt(field.value, 10) > parseInt(field.getAttribute('max'), 10))
rangeUnderflow
The rangeUnderflow property should return true if the field has a min attribute, is a numeric, and has at least one character that's under the min value. Like with rangeOverflow, we need to convert the string value of min to an integer using the parseInt() method.
rangeUnderflow: (field.hasAttribute('min') && isNum && field.value > 1 && parseInt(field.value, 10) < parseInt(field.getAttribute('min'), 10))
stepMismatch
For the stepMismatch property, if the field is numeric, has the step attribute, and the attribute's value isn't any, we'll use the remainder operator (%) to make sure that the field value divided by the step has no remainder. If there's a remainder, we'll return true.
stepMismatch: (field.hasAttribute('step') && field.getAttribute('step') !== 'any' && isNum && Number(field.value) % parseFloat(field.getAttribute('step')) !== 0)
tooLong
With tooLong, we'll return true if the field has a maxLength attribute greater than 0, and the field value length is greater than the attribute value.
<tooLong: (field.hasAttribute('maxLength') && field.getAttribute('maxLength') > 0 && length > parseInt(field.getAttribute('maxLength'), 10))
tooShort
Conversely, with tooShort, we'll return true if the field has a minLength attribute greater than 0, and the field value length is less than the attribute value.
tooShort: (field.hasAttribute('minLength') && field.getAttribute('minLength') > 0 && length > 0 && length < parseInt(field.getAttribute('minLength'), 10))
typeMismatch
The typeMismatch property is the most complicated to validate. We need to first make sure the field isn't empty. Then we need to run one regex text if the field type is email, and another if it's url. If it's one of those values and the field value does not match our regex pattern, we'll return true.
typeMismatch: (length > 0 && ((type === 'email' && !/^([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x22([^x0dx22x5cx80-xff]|x5c[x00-x7f])*x22)(x2e([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x22([^x0dx22x5cx80-xff]|x5c[x00-x7f])*x22))*x40([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x5b([^x0dx5b-x5dx80-xff]|x5c[x00-x7f])*x5d)(x2e([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x5b([^x0dx5b-x5dx80-xff]|x5c[x00-x7f])*x5d))*$/.test(field.value)) || (type === 'url' && !/^(?:(?:https?|HTTPS?|ftp|FTP)://)(?:S+(?::S*)?@)?(?:(?!(?:10|127)(?:.d{1,3}){3})(?!(?:169.254|192.168)(?:.d{1,3}){2})(?!172.(?:1[6-9]|2d|3[0-1])(?:.d{1,3}){2})(?:[1-9]d?|1dd|2[01]d|22[0-3])(?:.(?:1?d{1,2}|2[0-4]d|25[0-5])){2}(?:.(?:[1-9]d?|1dd|2[0-4]d|25[0-4]))|(?:(?:[a-zA-Zu00a1-uffff0-9]-*)*[a-zA-Zu00a1-uffff0-9]+)(?:.(?:[a-zA-Zu00a1-uffff0-9]-*)*[a-zA-Zu00a1-uffff0-9]+)*)(?::d{2,5})?(?:[/?#]S*)?$/.test(field.value))))
valueMissing
The valueMissing property is also a little complicated. First, we want to check if the field has the required attribute. If it does we need to run one of three few different tests, depending on the field type.
If it's a checkbox or radio button, we want to make sure that it's checked. If it's a select menu, we need to make sure a value is selected. If it's another type of input, we need to make sure it has a value.
valueMissing: (field.hasAttribute('required') && (((type === 'checkbox' || type === 'radio') && !field.checked) || (type === 'select' && field.options[field.selectedIndex].value < 1) || (type !=='checkbox' && type !== 'radio' && type !=='select' && length < 1)))
The complete set of tests
Here's what the completed checkValidity object looks like with all of its tests.
// Run validity checks
var checkValidity = {
badInput: (isNum && length > 0 && !/[-+]?[0-9]/.test(field.value)), // value of a number field is not a number
patternMismatch: (field.hasAttribute('pattern') && length > 0 && new RegExp(field.getAttribute('pattern')).test(field.value) === false), // value does not conform to the pattern
rangeOverflow: (field.hasAttribute('max') && isNum && field.value > 1 && parseInt(field.value, 10) > parseInt(field.getAttribute('max'), 10)), // value of a number field is higher than the max attribute
rangeUnderflow: (field.hasAttribute('min') && isNum && field.value > 1 && parseInt(field.value, 10) < parseInt(field.getAttribute('min'), 10)), // value of a number field is lower than the min attribute
stepMismatch: (field.hasAttribute('step') && field.getAttribute('step') !== 'any' && isNum && Number(field.value) % parseFloat(field.getAttribute('step')) !== 0), // value of a number field does not conform to the stepattribute
tooLong: (field.hasAttribute('maxLength') && field.getAttribute('maxLength') > 0 && length > parseInt(field.getAttribute('maxLength'), 10)), // the user has edited a too-long value in a field with maxlength
tooShort: (field.hasAttribute('minLength') && field.getAttribute('minLength') > 0 && length > 0 && length < parseInt(field.getAttribute('minLength'), 10)), // the user has edited a too-short value in a field with minlength
typeMismatch: (length > 0 && ((type === 'email' && !/^([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x22([^x0dx22x5cx80-xff]|x5c[x00-x7f])*x22)(x2e([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x22([^x0dx22x5cx80-xff]|x5c[x00-x7f])*x22))*x40([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x5b([^x0dx5b-x5dx80-xff]|x5c[x00-x7f])*x5d)(x2e([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x5b([^x0dx5b-x5dx80-xff]|x5c[x00-x7f])*x5d))*$/.test(field.value)) || (type === 'url' && !/^(?:(?:https?|HTTPS?|ftp|FTP)://)(?:S+(?::S*)?@)?(?:(?!(?:10|127)(?:.d{1,3}){3})(?!(?:169.254|192.168)(?:.d{1,3}){2})(?!172.(?:1[6-9]|2d|3[0-1])(?:.d{1,3}){2})(?:[1-9]d?|1dd|2[01]d|22[0-3])(?:.(?:1?d{1,2}|2[0-4]d|25[0-5])){2}(?:.(?:[1-9]d?|1dd|2[0-4]d|25[0-4]))|(?:(?:[a-zA-Zu00a1-uffff0-9]-*)*[a-zA-Zu00a1-uffff0-9]+)(?:.(?:[a-zA-Zu00a1-uffff0-9]-*)*[a-zA-Zu00a1-uffff0-9]+)*)(?::d{2,5})?(?:[/?#]S*)?$/.test(field.value)))), // value of a email or URL field is not an email address or URL
valueMissing: (field.hasAttribute('required') && (((type === 'checkbox' || type === 'radio') && !field.checked) || (type === 'select' && field.options[field.selectedIndex].value < 1) || (type !=='checkbox' && type !== 'radio' && type !=='select' && length < 1))) // required field without a value
};
Special considerations for radio buttons
In supporting browsers, required will only fail on a radio button if no elements in the group have been checked. Our polyfill as it's currently written will throw return valueMissing as true on an unchecked radio button even if another button in the group is checked.
To fix this, we need to get every button in the group. If one of them is checked, we'll validate that radio button instead of the one that lost focus.
// Generate the field validity object
var getValidityState = function (field) {

// Variables
var type = field.getAttribute('type') || input.nodeName.toLowerCase(); // The field type
var isNum = type === 'number' || type === 'range'; // Is the field numeric
var length = field.value.length; // The field value length

// If radio group, get selected field
if (field.type === 'radio' && field.name) {
var group = document.getElementsByName(field.name);
if (group.length > 0) {
for (var i = 0; i < group.length; i++) {
if (group[i].form === field.form && field.checked) {
field = group[i];
break;
}
}
}
}

...

};
Adding the validity property to form fields
Finally, if the Validity State API isn't fully supported, we want to add or override the validity property. We'll do this using the Object.defineProperty() method.
// If the full set of ValidityState features aren't supported, polyfill
if (!supported()) {
Object.defineProperty(HTMLInputElement.prototype, 'validity', {
get: function ValidityState() {
return getValidityState(this);
},
configurable: true,
});
}
Putting it all together
Here's the polyfill in its entirety. To keep our functions out of the global scope, I've wrapped it in an IIFE (immediately invoked function expression).
;(function (window, document, undefined) {

'use strict';

// Make sure that ValidityState is supported in full (all features)
var supported = function () {
var input = document.createElement('input');
return ('validity' in input && 'badInput' in input.validity && 'patternMismatch' in input.validity && 'rangeOverflow' in input.validity && 'rangeUnderflow' in input.validity && 'stepMismatch' in input.validity && 'tooLong' in input.validity && 'tooShort' in input.validity && 'typeMismatch' in input.validity && 'valid' in input.validity && 'valueMissing' in input.validity);
};

/**
* Generate the field validity object
* @param {Node]} field The field to validate
* @return {Object} The validity object
*/
var getValidityState = function (field) {

// Variables
var type = field.getAttribute('type') || input.nodeName.toLowerCase();
var isNum = type === 'number' || type === 'range';
var length = field.value.length;
var valid = true;

// Run validity checks
var checkValidity = {
badInput: (isNum && length > 0 && !/[-+]?[0-9]/.test(field.value)), // value of a number field is not a number
patternMismatch: (field.hasAttribute('pattern') && length > 0 && new RegExp(field.getAttribute('pattern')).test(field.value) === false), // value does not conform to the pattern
rangeOverflow: (field.hasAttribute('max') && isNum && field.value > 1 && parseInt(field.value, 10) > parseInt(field.getAttribute('max'), 10)), // value of a number field is higher than the max attribute
rangeUnderflow: (field.hasAttribute('min') && isNum && field.value > 1 && parseInt(field.value, 10) < parseInt(field.getAttribute('min'), 10)), // value of a number field is lower than the min attribute
stepMismatch: (field.hasAttribute('step') && field.getAttribute('step') !== 'any' && isNum && Number(field.value) % parseFloat(field.getAttribute('step')) !== 0), // value of a number field does not conform to the stepattribute
tooLong: (field.hasAttribute('maxLength') && field.getAttribute('maxLength') > 0 && length > parseInt(field.getAttribute('maxLength'), 10)), // the user has edited a too-long value in a field with maxlength
tooShort: (field.hasAttribute('minLength') && field.getAttribute('minLength') > 0 && length > 0 && length < parseInt(field.getAttribute('minLength'), 10)), // the user has edited a too-short value in a field with minlength
typeMismatch: (length > 0 && ((type === 'email' && !/^([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x22([^x0dx22x5cx80-xff]|x5c[x00-x7f])*x22)(x2e([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x22([^x0dx22x5cx80-xff]|x5c[x00-x7f])*x22))*x40([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x5b([^x0dx5b-x5dx80-xff]|x5c[x00-x7f])*x5d)(x2e([^x00-x20x22x28x29x2cx2ex3a-x3cx3ex40x5b-x5dx7f-xff]+|x5b([^x0dx5b-x5dx80-xff]|x5c[x00-x7f])*x5d))*$/.test(field.value)) || (type === 'url' && !/^(?:(?:https?|HTTPS?|ftp|FTP)://)(?:S+(?::S*)?@)?(?:(?!(?:10|127)(?:.d{1,3}){3})(?!(?:169.254|192.168)(?:.d{1,3}){2})(?!172.(?:1[6-9]|2d|3[0-1])(?:.d{1,3}){2})(?:[1-9]d?|1dd|2[01]d|22[0-3])(?:.(?:1?d{1,2}|2[0-4]d|25[0-5])){2}(?:.(?:[1-9]d?|1dd|2[0-4]d|25[0-4]))|(?:(?:[a-zA-Zu00a1-uffff0-9]-*)*[a-zA-Zu00a1-uffff0-9]+)(?:.(?:[a-zA-Zu00a1-uffff0-9]-*)*[a-zA-Zu00a1-uffff0-9]+)*)(?::d{2,5})?(?:[/?#]S*)?$/.test(field.value)))), // value of a email or URL field is not an email address or URL
valueMissing: (field.hasAttribute('required') && (((type === 'checkbox' || type === 'radio') && !field.checked) || (type === 'select' && field.options[field.selectedIndex].value < 1) || (type !=='checkbox' && type !== 'radio' && type !=='select' && length < 1))) // required field without a value
};

// Check if any errors
for (var key in checkValidity) {
if (checkValidity.hasOwnProperty(key)) {
// If there's an error, change valid value
if (checkValidity[key]) {
valid = false;
break;
}
}
}

// Add valid property to validity object
checkValidity.valid = valid;

// Return object
return checkValidity;

};

// If the full set of ValidityState features aren't supported, polyfill
if (!supported()) {
Object.defineProperty(HTMLInputElement.prototype, 'validity', {
get: function ValidityState() {
return getValidityState(this);
},
configurable: true,
});
}

})(window, document);
Adding this to your site will extend the Validity State API back to IE9, and add missing properties to partially supporting browsers. (You can download the polyfill on GitHub, too.)
The form validation script we wrote in the last article also made use of the classList API, which is supported in all modern browsers and IE10 and above. To truly get IE9+ support, we should also include the classList.js polyfill from Eli Grey.

Article Series:

Constraint Validation in HTML
The Constraint Validation API in JavaScript
A Validity State API Polyfill (You are here!)
Validating the MailChimp Subscribe Form (Coming Soon!)

Form Validation Part 3: A Validity State API Polyfill is a post from CSS-Tricks
Source: CssTricks


Form Validation Part 2: The Constraint Validation API (JavaScript)

In my last article, I showed you how to use native browser form validation through a combination of semantic input types (for example, <input type="email">) and validation attributes (such as required and pattern).
While incredibly easy and super lightweight, this approach does have a few shortcomings.

You can style fields that have errors on them with the :invalid pseudo-selector, but you can't style the error messages themselves.
Behavior is also inconsistent across browsers.

User studies from Christian Holst and Luke Wroblewski (separately) found that displaying an error when the user leaves a field, and keeping that error persistent until the issue is fixed, provided the best and fastest user experience.
Unfortunately, none of the browsers natively behave this way. However, there is a way to get this behavior without depending on a large JavaScript form validation library.

Article Series:

Constraint Validation in HTML
The Constraint Validation API in JavaScript (You are here!)
A Validity State API Polyfill (Coming Soon!)
Validating the MailChimp Subscribe Form (Coming Soon!)

The Constraint Validation API
In addition to HTML attributes, browser-native constraint validation also provides a JavaScript API we can use to customize our form validation behavior.
There are a few different methods the API exposes, but the most powerful, Validity State, allows us to use the browser's own field validation algorithms in our scripts instead of writing our own.
In this article, I'm going to show you how to use Validity State to customize the behavior, appearance, and content of your form validation error messages.
Validity State
The validity property provides a set of information about a form field, in the form of boolean (true/false) values.
var myField = document.querySelector('input[type="text"]');
var validityState = myField.validity;
The returned object contains the following properties:

valid - Is true when the field passes validation.
valueMissing - Is true when the field is empty but required.
typeMismatch - Is true when the field type is email or url but the entered value is not the correct type.
tooShort - Is true when the field contains a minLength attribute and the entered value is shorter than that length.
tooLong - Is true when the field contains a maxLength attribute and the entered value is longer than that length.
patternMismatch - Is true when the field contains a pattern attribute and the entered value does not match the pattern.
badInput - Is true when the input type is number and the entered value is not a number.
stepMismatch - Is true when the field has a step attribute and the entered value does not adhere to the step values.
rangeOverflow - Is true when the field has a max attribute and the entered number value is greater than the max.
rangeUnderflow - Is true when the field has a min attribute and the entered number value is lower than the min.

By using the validity property in conjunction with our input types and HTML validation attributes, we can build a robust form validation script that provides a great user experience with a relatively small amount of JavaScript.
Let's get to it!
Disable native form validation
Since we're writing our validation script, we want to disable the native browser validation by adding the novalidate attribute to our forms. We can still use the Constraint Validation API — we just want to prevent the native error messages from displaying.
As a best practice, we should add this attribute with JavaScript so that if our script has an error or fails to load, the native browser form validation will still work.
// Add the novalidate attribute when the JS loads
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].setAttribute('novalidate', true);
}
There may be some forms that you don't want to validate (for example, a search form that shows up on every page). Rather than apply our validation script to all forms, let's apply it just to forms that have the .validate class.
// Add the novalidate attribute when the JS loads
var forms = document.querySelectorAll('.validate');
for (var i = 0; i < forms.length; i++) {
forms[i].setAttribute('novalidate', true);
}
See the Pen Form Validation: Add `novalidate` programatically by Chris Ferdinandi (@cferdinandi) on CodePen.
Check validity when the user leaves the field
Whenever a user leaves a field, we want to check if it's valid. To do this, we'll setup an event listener.
Rather than add a listener to every form field, we'll use a technique called event bubbling (or event propagation) to listen for all blur events.
// Listen to all blur events
document.addEventListener('blur', function (event) {
// Do something on blur...
}, true);
You'll note that the last argument in addEventListener is set to true. This argument is called useCapture, and it's normally set to false. The blur event doesn't bubble the way events like click do. Setting this argument to true allows us to capture all blur events rather than only those that happen directly on the element we're listening to.
Next, we want to make sure that the blurred element was a field in a form with the .validate class. We can get the blurred element using event.target, and get it's parent form by calling event.target.form. Then we'll use classList to check if the form has the validation class or not.
If it does, we can check the field validity.
// Listen to all blur events
document.addEventListener('blur', function (event) {

// Only run if the field is in a form to be validated
if (!event.target.form.classList.contains('validate')) return;

// Validate the field
var error = event.target.validity;
console.log(error);

}, true);
If error.validity is true, the field is valid. Otherwise, there's an error.
See the Pen Form Validation: Validate On Blur by Chris Ferdinandi (@cferdinandi) on CodePen.
Getting the error
Once we know there's an error, it's helpful to know what the error actually is. We can use the other Validity State properties to get that information.
Since we need to check each property, the code for this can get a bit long. Let's setup a separate function for this and pass our field into it.
// Validate the field
var hasError = function (field) {
// Get the error
};

// Listen to all blur events
document.addEventListner('blur', function (event) {

// Only run if the field is in a form to be validated
if (!event.target.form.classList.contains('validate')) return;

// Validate the field
var error = hasError(event.target);

}, true);
There are a few field types we want to ignore: fields that are disabled, file and reset inputs, and submit inputs and buttons. If a field isn't one of those, let's get it's validity.
// Validate the field
var hasError = function (field) {

// Don't validate submits, buttons, file and reset inputs, and disabled fields
if (field.disabled || field.type === 'file' || field.type === 'reset' || field.type === 'submit' || field.type === 'button') return;

// Get validity
var validity = field.validity;

};
If there's no error, we'll return null. Otherwise, we'll check each of the Validity State properties until we find the error.
When we find the match, we'll return a string with the error. If none of the properties are true but validity is false, we'll return a generic "catchall" error message (I can't imagine a scenario where this happens, but it's good to plan for the unexpected).
// Validate the field
var hasError = function (field) {

// Don't validate submits, buttons, file and reset inputs, and disabled fields
if (field.disabled || field.type === 'file' || field.type === 'reset' || field.type === 'submit' || field.type === 'button') return;

// Get validity
var validity = field.validity;

// If valid, return null
if (validity.valid) return;

// If field is required and empty
if (validity.valueMissing) return 'Please fill out this field.';

// If not the right type
if (validity.typeMismatch) return 'Please use the correct input type.';

// If too short
if (validity.tooShort) return 'Please lengthen this text.';

// If too long
if (validity.tooLong) return 'Please shorten this text.';

// If number input isn't a number
if (validity.badInput) return 'Please enter a number.';

// If a number value doesn't match the step interval
if (validity.stepMismatch) return 'Please select a valid value.';

// If a number field is over the max
if (validity.rangeOverflow) return 'Please select a smaller value.';

// If a number field is below the min
if (validity.rangeUnderflow) return 'Please select a larger value.';

// If pattern doesn't match
if (validity.patternMismatch) return 'Please match the requested format.';

// If all else fails, return a generic catchall error
return 'The value you entered for this field is invalid.';

};
This is a good start, but we can do some additional parsing to make a few of our errors more useful. For typeMismatch, we can check if it's supposed to be an email or url and customize the error accordingly.
// If not the right type
if (validity.typeMismatch) {

// Email
if (field.type === 'email') return 'Please enter an email address.';

// URL
if (field.type === 'url') return 'Please enter a URL.';

}
If the field value is too long or too short, we can find out both how long or short it's supposed to be and how long or short it actually is. We can then include that information in the error.
// If too short
if (validity.tooShort) return 'Please lengthen this text to ' + field.getAttribute('minLength') + ' characters or more. You are currently using ' + field.value.length + ' characters.';

// If too long
if (validity.tooLong) return 'Please short this text to no more than ' + field.getAttribute('maxLength') + ' characters. You are currently using ' + field.value.length + ' characters.';
If a number field is over or below the allowed range, we can include that minimum or maximum allowed value in our error.
// If a number field is over the max
if (validity.rangeOverflow) return 'Please select a value that is no more than ' + field.getAttribute('max') + '.';

// If a number field is below the min
if (validity.rangeUnderflow) return 'Please select a value that is no less than ' + field.getAttribute('min') + '.';
And if there is a pattern mismatch and the field has a title, we can use that as our error, just like the native browser behavior.
// If pattern doesn't match
if (validity.patternMismatch) {

// If pattern info is included, return custom error
if (field.hasAttribute('title')) return field.getAttribute('title');

// Otherwise, generic error
return 'Please match the requested format.';

}
Here's the complete code for our hasError() function.
// Validate the field
var hasError = function (field) {

// Don't validate submits, buttons, file and reset inputs, and disabled fields
if (field.disabled || field.type === 'file' || field.type === 'reset' || field.type === 'submit' || field.type === 'button') return;

// Get validity
var validity = field.validity;

// If valid, return null
if (validity.valid) return;

// If field is required and empty
if (validity.valueMissing) return 'Please fill out this field.';

// If not the right type
if (validity.typeMismatch) {

// Email
if (field.type === 'email') return 'Please enter an email address.';

// URL
if (field.type === 'url') return 'Please enter a URL.';

}

// If too short
if (validity.tooShort) return 'Please lengthen this text to ' + field.getAttribute('minLength') + ' characters or more. You are currently using ' + field.value.length + ' characters.';

// If too long
if (validity.tooLong) return 'Please shorten this text to no more than ' + field.getAttribute('maxLength') + ' characters. You are currently using ' + field.value.length + ' characters.';

// If number input isn't a number
if (validity.badInput) return 'Please enter a number.';

// If a number value doesn't match the step interval
if (validity.stepMismatch) return 'Please select a valid value.';

// If a number field is over the max
if (validity.rangeOverflow) return 'Please select a value that is no more than ' + field.getAttribute('max') + '.';

// If a number field is below the min
if (validity.rangeUnderflow) return 'Please select a value that is no less than ' + field.getAttribute('min') + '.';

// If pattern doesn't match
if (validity.patternMismatch) {

// If pattern info is included, return custom error
if (field.hasAttribute('title')) return field.getAttribute('title');

// Otherwise, generic error
return 'Please match the requested format.';

}

// If all else fails, return a generic catchall error
return 'The value you entered for this field is invalid.';

};
Try it yourself in the pen below.
See the Pen Form Validation: Get the Error by Chris Ferdinandi (@cferdinandi) on CodePen.
Show an error message
Once we get our error, we can display it below the field. We'll create a showError() function to handle this, and pass in our field and the error. Then, we'll call it in our event listener.
// Show the error message
var showError = function (field, error) {
// Show the error message...
};

// Listen to all blur events
document.addEventListener('blur', function (event) {

// Only run if the field is in a form to be validated
if (!event.target.form.classList.contains('validate')) return;

// Validate the field
var error = hasError(event.target);

// If there's an error, show it
if (error) {
showError(event.target, error);
}

}, true);
In our showError function, we're going to do a few things:

We'll add a class to the field with the error so that we can style it.
If an error message already exists, we'll update it with new text.
Otherwise, we'll create a message and inject it into the DOM immediately after the field.

We'll also use the field ID to create a unique ID for the message so we can find it again later (falling back to the field name in case there's no ID).
var showError = function (field, error) {

// Add error class to field
field.classList.add('error');

// Get field id or name
var id = field.id || field.name;
if (!id) return;

// Check if error message field already exists
// If not, create one
var message = field.form.querySelector('.error-message#error-for-' + id );
if (!message) {
message = document.createElement('div');
message.className = 'error-message';
message.id = 'error-for-' + id;
field.parentNode.insertBefore( message, field.nextSibling );
}

// Update error message
message.innerHTML = error;

// Show error message
message.style.display = 'block';
message.style.visibility = 'visible';

};
To make sure that screen readers and other assistive technology know that our error message is associated with our field, we also need to add the aria-describedby role.
var showError = function (field, error) {

// Add error class to field
field.classList.add('error');

// Get field id or name
var id = field.id || field.name;
if (!id) return;

// Check if error message field already exists
// If not, create one
var message = field.form.querySelector('.error-message#error-for-' + id );
if (!message) {
message = document.createElement('div');
message.className = 'error-message';
message.id = 'error-for-' + id;
field.parentNode.insertBefore( message, field.nextSibling );
}

// Add ARIA role to the field
field.setAttribute('aria-describedby', 'error-for-' + id);

// Update error message
message.innerHTML = error;

// Show error message
message.style.display = 'block';
message.style.visibility = 'visible';

};
Style the error message
We can use the .error and .error-message classes to style our form field and error message.
As a simple example, you may want to display a red border around fields with an error, and make the error message red and italicized.
.error {
border-color: red;
}

.error-message {
color: red;
font-style: italic;
}
See the Pen Form Validation: Display the Error by Chris Ferdinandi (@cferdinandi) on CodePen.
Hide an error message
Once we show an error, your visitor will (hopefully) fix it. Once the field validates, we need to remove the error message. Let's create another function, removeError(), and pass in the field. We'll call this function from event listener as well.
// Remove the error message
var removeError = function (field) {
// Remove the error message...
};

// Listen to all blur events
document.addEventListener('blur', function (event) {

// Only run if the field is in a form to be validated
if (!event.target.form.classList.contains('validate')) return;

// Validate the field
var error = event.target.validity;

// If there's an error, show it
if (error) {
showError(event.target, error);
return;
}

// Otherwise, remove any existing error message
removeError(event.target);

}, true);
In removeError(), we want to:

Remove the error class from our field.
Remove the aria-describedby role from the field.
Hide any visible error messages in the DOM.

Because we could have multiple forms on a page, and there's a chance those forms might have fields with the same name or ID (even though that's invalid, it happens), we're going to limit our search for the error message with querySelector the form our field is in rather than the entire document.
// Remove the error message
var removeError = function (field) {

// Remove error class to field
field.classList.remove('error');

// Remove ARIA role from the field
field.removeAttribute('aria-describedby');

// Get field id or name
var id = field.id || field.name;
if (!id) return;

// Check if an error message is in the DOM
var message = field.form.querySelector('.error-message#error-for-' + id + '');
if (!message) return;

// If so, hide it
message.innerHTML = '';
message.style.display = 'none';
message.style.visibility = 'hidden';

};
See the Pen Form Validation: Remove the Error After It's Fixed by Chris Ferdinandi (@cferdinandi) on CodePen.

If the field is a radio button or checkbox, we need to change how we add our error message to the DOM.
The field label often comes after the field, or wraps it entirely, for these types of inputs. Additionally, if the radio button is part of a group, we want the error to appear after the group rather than just the radio button.
See the Pen Form Validation: Issues with Radio Buttons & Checkboxes by Chris Ferdinandi (@cferdinandi) on CodePen.
First, we need to modify our showError() method. If the field type is radio and it has a name, we want get all radio buttons with that same name (ie. all other radio buttons in the group) and reset our field variable to the last one in the group.
// Show the error message
var showError = function (field, error) {

// Add error class to field
field.classList.add('error');

// If the field is a radio button and part of a group, error all and get the last item in the group
if (field.type === 'radio' && field.name) {
var group = document.getElementsByName(field.name);
if (group.length > 0) {
for (var i = 0; i < group.length; i++) {
// Only check fields in current form
if (group[i].form !== field.form) continue;
group[i].classList.add('error');
}
field = group[group.length - 1];
}
}

...

};
When we go to inject our message into the DOM, we first want to check if the field type is radio or checkbox. If so, we want to get the field label and inject our message after it instead of after the field itself.
// Show the error message
var showError = function (field, error) {

...

// Check if error message field already exists
// If not, create one
var message = field.form.querySelector('.error-message#error-for-' + id );
if (!message) {
message = document.createElement('div');
message.className = 'error-message';
message.id = 'error-for-' + id;

// If the field is a radio button or checkbox, insert error after the label
var label;
if (field.type === 'radio' || field.type ==='checkbox') {
label = field.form.querySelector('label[for="' + id + '"]') || field.parentNode;
if (label) {
label.parentNode.insertBefore( message, label.nextSibling );
}
}

// Otherwise, insert it after the field
if (!label) {
field.parentNode.insertBefore( message, field.nextSibling );
}
}

...

};
When we go to remove the error, we similarly need to check if the field is a radio button that's part of a group, and if so, use the last radio button in that group to get the ID of our error message.
// Remove the error message
var removeError = function (field) {

// Remove error class to field
field.classList.remove('error');

// If the field is a radio button and part of a group, remove error from all and get the last item in the group
if (field.type === 'radio' && field.name) {
var group = document.getElementsByName(field.name);
if (group.length > 0) {
for (var i = 0; i < group.length; i++) {
// Only check fields in current form
if (group[i].form !== field.form) continue;
group[i].classList.remove('error');
}
field = group[group.length - 1];
}
}

...

};
See the Pen Form Validation: Fixing Radio Buttons & Checkboxes by Chris Ferdinandi (@cferdinandi) on CodePen.
Checking all fields on submit
When a visitor submits our form, we should first validate every field in the form and display error messages on any invalid fields. We should also bring the first field with an error into focus so that the visitor can immediately take action to correct it.
We'll do this by adding a listener for the submit event.
// Check all fields on submit
document.addEventListener('submit', function (event) {
// Validate all fields...
}, false);
If the form has the .validate class, we'll get every field, loop through each one, and check for errors. We'll store the first invalid field we find to a variable and bring it into focus when we're done. If no errors are found, the form can submit normally.
// Check all fields on submit
document.addEventListener('submit', function (event) {

// Only run on forms flagged for validation
if (!event.target.classList.contains('validate')) return;

// Get all of the form elements
var fields = event.target.elements;

// Validate each field
// Store the first field with an error to a variable so we can bring it into focus later
var error, hasErrors;
for (var i = 0; i < fields.length; i++) {
error = hasError(fields[i]);
if (error) {
showError(fields[i], error);
if (!hasErrors) {
hasErrors = fields[i];
}
}
}

// If there are errrors, don't submit form and focus on first element with error
if (hasErrors) {
event.preventDefault();
hasErrors.focus();
}

// Otherwise, let the form submit normally
// You could also bolt in an Ajax form submit process here

}, false);
See the Pen Form Validation: Validate on Submit by Chris Ferdinandi (@cferdinandi) on CodePen.
Tying it all together
Our finished script weight just 6kb (2.7kb minified). You can download a plugin version on GitHub.
It works in all modern browsers and provides support IE support back to IE10. But, there are some browser gotchas…

Because we can't have nice things, not every browser supports every Validity State property.
Internet Explorer is, of course, the main violator, though Edge does lack support for tooLong even though IE10+ supports it. Go figure.

Here's the good news: with a lightweight polyfill (5kb, 2.7kb minified) we can extend our browser support all the way back to IE9, and add missing properties to partially supporting browsers, without having to touch any of our core code.
There is one exception to the IE9 support: radio buttons. IE9 doesn't support CSS3 selectors (like [name="' + field.name + '"]). We use that to make sure at least one radio button has been selected within a group. IE9 will always return an error.
I'll show you how to create this polyfill in the next article.

Article Series:

Constraint Validation in HTML
The Constraint Validation API in JavaScript (You are here!)
A Validity State API Polyfill (Coming Soon!)
Validating the MailChimp Subscribe Form (Coming Soon!)

Form Validation Part 2: The Constraint Validation API (JavaScript) is a post from CSS-Tricks
Source: CssTricks


The $1,000 Podcasting Setup

I figure between (as I write) the 267 episodes of ShopTalk, 134 episodes of CodePen Radio, 154 video screencasts (and many hundreds more as part of the different series), and all my guest podcast apperances, I'm edging on 1,000 things I've voice-recorded for public consumption.
98% of that was with the Rode Podcaster, the same exact microphone I documented using in 2008. I figured it was about time for an upgrade, as I plan to continue podcasting and screencasting for another 10 years! I know audio quality is a big deal. I don't quite consider myself an audiophile, but I know I prefer listening to podcasts with good audio quality, and I'd like to make sure what I produce is as good of quality as is practical for me right now.

I had our podcast editor Chris Enns on ShopTalk to talk gear'n'stuff, and this setup is largely his recommendations. A good part of the beauty of this setup is that it's designed around making it sound like you're in an amazing studio, without actually having to be.
Shure SM7B ($399)
Pictured here with the big alternate big fluffy cover that it ships with, which is helpful for minimizing breathing noises and pop filtering.
As Shure says, the SM7B has:
flat, wide-range frequency response for exceptionally clean and natural reproduction of both music and speech.
It's a pretty dang good microphone. (Chris Enns also recommended the Heil PR 40, which is in the same range.) On my desk, I have a Swivel Mount Boom Arm to put it on, so I can swing it into place when using it, and swing it away when I'm not.
Like most pretty dang good microphones, it's not "USB". It has an XLR plug, and you'll need an XLR cable to plug it into our next device...
DBX 286s ($196)

Chris Enns described DBX 286s to me as a sound sweetener.
It's an optional bit, but plugging the microphone into this first does a bunch of nice stuff for you. It's a big part of the "sound like your in a nice studio when you aren't" situation.
It looks kind of scary, since there are a whole bunch of knobs and buttons on it and they all actually do things. I found setup videos like this helpful:
[youtube https://www.youtube.com/watch?v=xG9UMMwu5wc&w=560&h=315]
The DBX 286x outputs in a 1/4" cable, so you'll need a XLR Male to 1/4" Male to plug it into...
Focusrite Scarlett 2i4 ($189)

This is the device that you actually plugin into your computer via USB. Your computer will recognize it as a sound input device.
The Focusrite Scarlett comes in a variety of models, mostly differentiated by how many inputs it has. If you know you'll only ever need one input, the Solo model has you covered at $99. I went for the 2i4 model which has two microphone inputs and four instrument inputs, just in case I want to do something a bit more robust with it at some point. Even just having a second podcast guest in the same vicinity, you could pipe them into one computer and get separate tracks, which is cool.
With the DBX 286s, you won't need any gain from the Focusrite Scarlett, but if you skip the DBX 286s (which you totally can), you will.
Setup
On my desk, I have it all stacked up like this:

That's Shure SM7B > DBX 286s > Scarlett Focusrite > Computer.
Then I use Audio Hijack Pro to record, so I can get the mono-audio recorded on both left and right channels properly.

All Together
Here's an Amazon List with it all there, if you're interested in the same kind of setup.

The $1,000 Podcasting Setup is a post from CSS-Tricks
Source: CssTricks


CSS-Tricks Chronicle XXXI

All the latest happenings! As I like to do, I round up a bunch of things that have happened in the past few months around here on this site, over at CodePen and ShopTalk, and other sites where I got to be a guest or was involved somehow. There has been some big releases, some redesigns, and a bunch of guest podcasts.

I got to be a guest on Relative Paths with Mark Phoenix and Ben Hutchings. It was episode 47 and the topic was dogmatism, a topic I weighed in on earlier with my post My Increasing Wariness of Dogmatism.

The biggest release ever on CodePen is CodePen Projects. It hasn't even been out three months yet! As opposed to Pens on CodePen, Projects gives you an editor that is more of a full-on IDE with your own file system.

I was a guest on Eric Siu's podcast Growth Everywhere, Episode 196 where we talk numbers and growth stuff. (Fair warning on the link: it's pretty pop-up heavy.)

I also had a lot of fun on the Email Design Podcast, Episode 60, where I got to chat with Kevin Mandeville and Jason Rodriguez specifically about email stuff. That's not something I get to talk about much, but I actually find myself doing quite a bit lately with email, and it's a very weird world that somehow feels completely different than "normal" front-end development.

I have moved back home to Milwaukee, after spending the last 7 months in Miami. Bittersweet! Farewell, friends old and new in Miami. Hello, friends old and new in Milwaukee.

We're less than a month away from the 10 year anniversary of CSS-Tricks! We'll definitely do something. No rooftop party or anything, but definitely come see what we got on July 4th.

I redesigned my personal site. It's nothing special to look at, but I think it's going to serve my needs very well. The new site needed to clearly show: this is who I am, this is what I do, this is where I exist other places on the web, and most importantly, these are the things I want you to do.
The most fun little bit is the radio buttons by the bio area, which allow you to customize the length, first person or third, and what format it's in.

ShopTalk also has a brand new website. Also designed and implemented by me, so, brace yourselves for utilitarian. This one was driven by backend data. I think I'll write about that soon.
We recently did a podcast called On Podcasting where I got to chat with Chris Enns about podcasting equipment. I figured it was about time to get some advice and update my gear. I bet if you factor in all the ShopTalk, CodePen Radio, guest appearances, and videos I've recorded, I'm around 1,000 episodes of stuff. Probably about time I have some decent gear. I pulled the trigger on the major upgrade. I'll have to post about that soon as well.

My public speaking schedule for the rest of the year is:

June 16, 2017, CSS Day, Amsterdam, NL
August 7-9, 2017, That Conference, Wisconsin Dells, WI
August 17, 2017, Build Right: Maker Series, Dayton, Ohio
August 28-30, 2017, An Event Apart, Chicago, IL
September 25-26, 2017, Web Unleashed, Toronto, Canada
October 9-11, 2017, CSS Dev Conf, New Orleans, LA
October 12-13, 2017, Profitable Developer Bootcamp, Milwaukee, WI
October 17–18, 2017, Smashing Conf Barcelona, Spain
October 30-November 1, 2017, An Event Apart, San Francisco, CA
December 11-13, 2017, An Event Apart, Denver, CO

CSS-Tricks Chronicle XXXI is a post from CSS-Tricks
Source: CssTricks


React Forms: Using Refs

React provides two standard ways to grab values from <form> elements. The first method is to implement what are called controlled components (see my blog post on the topic) and the second is to use React's ref property.
Controlled components are heavy duty. The defining characteristic of a controlled component is the displayed value is bound to component state. To update the value, you execute a function attached to the onChange event handler on the form element. The onChange function updates the state property, which in turn updates the form element's value.
(Before we get too far, if you just want to see the code samples for this article: here you go!)

Here's an example of a controlled component:
import React, { Component } from 'react';

class ControlledCompExample extends Component {
constructor() {
super();
this.state = {
fullName: ''
}
}
handleFullNameChange = (e) => {
this.setState({
fullName: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault();
console.log(this.state.fullName)
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label htmlFor="fullName">Full Name</label>
<input
type="text"
value={this.state.fullName}
onChange={this.handleFullNameChange}
name="fullName" />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}

export default ControlledCompExample;
The value of the input is this.state.fullName (lines 7 and 26). The onChange function is handleFullNameChange (lines 10 - 14, and line 27).
The main advantages of controlled components are:

You are set up to easily validate user input.
You can dynamically render other components based on the value of the controlled component. For example, the value a user selects from a dropdown (e.g. 'dog' or 'cat') can control which other form components (e.g. a checkbox set of breeds) are rendered in the form.

The downside to controlled components is the amount of code you have to write. You need a state property to pass to the form element as props, and you need a function to update the value of this property.
For one form element this isn't an issue – but if you have a large, complex form (that doesn't need dynamic rendering or real-time validation), you'll find yourself writing a ton of code if you overuse controlled components.
An easier and less labor-intensive way to grab values from a form element is to use the ref property. Different form elements and component compositions require different strategies, so the rest of this post is divided into the following sections.

Text inputs, number inputs, and selects
Passing props from child to parent
Radio sets
Checkbox sets

1. Text inputs, number inputs, and selects
Text and number inputs provide the most straightforward example of using refs. In the ref attribute of the input, add an arrow function that takes the input as an argument. I tend to name the argument the same as the element itself as seen on line 3 below:
<input
type="text"
ref={input => this.fullName = input} />
Since it's an alias for the input element itself, you can name the argument whatever you'd like:
<input
type="number"
ref={cashMoney => this.amount = cashMoney} />
You then take the argument and assign it to a property attached to the class's this keyword. The inputs (i.e. the DOM node) are now accessible as this.fullName and this.amount. The values of the inputs are accessible as this.fullName.value and this.amount.value.
The same strategy works for select elements (i.e. dropdowns).
<select
ref={select => this.petType = select}
name="petType">
<option value="cat">Cat</option>
<option value="dog">Dog</option>
<option value="ferret">Ferret</option>
</select>
The value selected is accessible as this.petType.value.
2. Passing props from child to parent
With a controlled component, getting the value from a child component to a parent is straightforward – the value already lives in the parent! It's passed down to the child. An onChange function is also passed down and updates the value as the user interacts with the UI.
You can see this at work in the controlled component examples in my previous post.
While the value already lives in the parent's state in controlled components, this is not so when using refs. With refs, the value resides in the DOM node itself, and must be communicated up to the parent.
To pass this value from child to parent, the parent needs to pass down a 'hook', if you will, to the child. The child then attaches a node to the 'hook' so the parent has access to it.
Let's look at some code before discussing this further.
import React, { Component } from 'react';

class RefsForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
console.log('first name:', this.firstName.value);
this.firstName.value = 'Got ya!';
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<CustomInput
label={'Name'}
firstName={input => this.firstName = input} />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}

function CustomInput(props) {
return (
<div>
<label>{props.label}:</label>
<input type="text" ref={props.firstName}/>
</div>
);
}

export default RefsForm;
Above you see a form component RefForm, and an input component called CustomInput. Usually, the arrow function is on the input itself, but here it's being passed down as a prop (see lines 15 and 27). Since the arrow function resides in the parent, the this of this.firstName lives in the parent.
The value of the child input is being assigned to the this.firstName property of the parent, so the child's value is available to the parent. Now, in the parent, this.firstName refers to a DOM node in the child component (i.e. the input in CustomInput).
Not only can the DOM node of the input be accessed by the parent, but the value of the node can also be assigned from within the parent. This is demonstrated on line 7 above. Once the form is submitted, the value of the input is set to 'Got ya!'.
This pattern is a bit mind bending, so stare at it for a while and play around with the code until it sinks in.
You may be better off making radios and checkboxes controlled components, but if you really want to use refs the next two sections are for you.
3. Radio sets
Unlike text and number input elements, radios come in sets. Each element in a set has the same name attribute, like so:
<form>
<label>
Cat
<input type="radio" value="cat" name="pet" />
</label>
<label>
Dog
<input type="radio" value="dog" name="pet" />
</label>
<label>
Ferret
<input type="radio" value="ferret" name="pet" />
</label>
<input type="submit" value="Submit" />
</form>
There are three options in the "pet" radio set – "cat", "dog", and "ferret".
Since the whole set is the object of our concern, setting a ref on each radio input is not ideal. And, unfortunately, there's no DOM node that encapsulates a set of radios.
Retrieving the value of the radio set can be obtained through three steps:

Set a ref on the <form> tag (line 20 below).
Extract the set of radios from the form. In this case, it is the pet set (line 9 below).

A node list and a value is returned here. In this case, this node list includes three input nodes, and the value selected.
Keep in mind that a node list looks like an array but is not, and lacks array methods. There's more on this topic in the next section.

Grab the value of the set using dot notation (line 13 below).

import React, { Component } from 'react';

class RefsForm extends Component {

handleSubmit = (e) => {
e.preventDefault();

// extract the node list from the form
// it looks like an array, but lacks array methods
const { pet } = this.form;

// a set of radios has value property
// checkout out the log for proof
console.log(pet, pet.value);
}

render() {
return (
<div>
<form
onSubmit={this.handleSubmit}
ref={form => this.form = form}>
<label>
Cat
<input type="radio" value="cat" name="pet" />
</label>
<label>
Dog
<input type="radio" value="dog" name="pet" />
</label>
<label>
Ferret
<input type="radio" value="ferret" name="pet" />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}

export default RefsForm;
This works even if you are composing a form from children components. Although there's more logic in the components, the technique for getting the value from the radio set remains the same.
import React, { Component } from 'react';

class RefsForm extends Component {
handleSubmit = (e) => {
e.preventDefault();

// extract the node list from the form
// it looks like an array, but lacks array methods
const { pet } = this.form;

// a set of radios has value property
// checkout out the log for proof
console.log(pet, pet.value);
}

render() {
return (
<div>
<form
onSubmit={this.handleSubmit}
ref={form => this.form = form}>
<RadioSet
setName={'pet'}
setOptions={['cat', 'dog', 'ferret']} />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}

function RadioSet(props) {
return (
<div>
{props.setOptions.map(option => {
return (
<label
key={option}
style={{textTransform: 'capitalize'}}>
{option}
<input
type="radio"
value={option}
name={props.setName} />
</label>
)
})}
</div>
);
}

export default RefsForm;
4. Checkbox sets
Unlike a radio set, a checkbox set may have multiple values selected. This makes extracting these values a little more complicated than extracting the value of a radio set.
Retrieving the selected values of the checkbox set can be done through these five steps:

Set a ref on the <form> tag (line 27 below).
Extract the set of checkboxes from the form. In this case, it is the pet set (line 9).

A node list and a value is returned here.
Keep in mind that a node list looks like an array but is not, and lacks array methods, which takes us to the next step...

Convert the node list to an array, so array methods are available (checkboxArray on line 12).
Use Array.filter() to grab only the checked checkboxes (checkedCheckboxes on line 15).
Use Array.map() to keep only the values of the checked checkboxes (checkedCheckboxesValues on line 19).

import React, { Component } from 'react';

class RefsForm extends Component {
handleSubmit = (e) => {
e.preventDefault();

// extract the node list from the form
// it looks like an array, but lacks array methods
const { pet } = this.form;

// convert node list to an array
const checkboxArray = Array.prototype.slice.call(pet);

// extract only the checked checkboxes
const checkedCheckboxes = checkboxArray.filter(input => input.checked);
console.log('checked array:', checkedCheckboxes);

// use .map() to extract the value from each checked checkbox
const checkedCheckboxesValues = checkedCheckboxes.map(input => input.value);
console.log('checked array values:', checkedCheckboxesValues);
}

render() {
return (
<div>
<form
onSubmit={this.handleSubmit}
ref={form => this.form = form}>
<label>
Cat
<input type="checkbox" value="cat" name="pet" />
</label>
<label>
Dog
<input type="checkbox" value="dog" name="pet" />
</label>
<label>
Ferret
<input type="checkbox" value="ferret" name="pet" />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}

export default RefsForm;
Using a checkbox set child component works just like the radio set example in the previous section.
import React, { Component } from 'react';

class RefsForm extends Component {
handleSubmit = (e) => {
e.preventDefault();

// extract the node list from the form
// it looks like an array, but lacks array methods
const { pet } = this.form;

// convert node list to an array
const checkboxArray = Array.prototype.slice.call(pet);

// extract only the checked checkboxes
const checkedCheckboxes = checkboxArray.filter(input => input.checked);
console.log('checked array:', checkedCheckboxes);

// use .map() to extract the value from each checked checkbox
const checkedCheckboxesValues = checkedCheckboxes.map(input => input.value);
console.log('checked array values:', checkedCheckboxesValues);
}

render() {
return (
<div>
<form
onSubmit={this.handleSubmit}
ref={form => this.form = form}>
<CheckboxSet
setName={'pet'}
setOptions={['cat', 'dog', 'ferret']} />
<input type="submit" value="Submit" />
</form>
</div>
);
}
}

function CheckboxSet(props) {
return (
<div>
{props.setOptions.map(option => {
return (
<label
key={option}
style={{textTransform: 'capitalize'}}>
{option}
<input
type="checkbox"
value={option}
name={props.setName} />
</label>
)
})}
</div>
);
}

export default RefsForm;
Conclusion
If you don't need to:

monitor the value of a form element in real-time (e.g. in order to render subsequent components based on user input), or
perform custom validation in real-time,

then using refs to grab data from form elements is a good bet.
The primary value of using refs over controlled component is that, in most cases, you will write less code. The exceptional case is that of checkbox sets (and radios to a lesser degree). For checkbox sets, the amount of code you save by using refs is minimal, so it's less clear whether to use a controlled component or refs.

React Forms: Using Refs is a post from CSS-Tricks
Source: CssTricks


11 ways to get feedback from your most introverted employee

I’m an introvert. Most people don’t believe me when I tell them this (I do a lot of public speaking for an introvert 😳), but I am an introvert through-and-through. When I need to recharge, I seek alone time as opposed to being around a group people.As an introvert, I also tend to avoid questions that seem overly personal or require long, drawn-out answers. Honestly, it can feel draining to divulge so much of myself and talk all the time.Early in my career, I remember how this played out at work: My boss at the time would ask me for feedback about the company… And I struggled to answer his questions candidly. As introvert, I never felt comfortable being 100% transparent with him about what I thought could be better in the company.I’m embarrassed thinking back on what was probably viewed as avoidant, disengaged behavior. It’s not that I didn’t want to be honest — I just didn’t know how.Little did I know that, on the other end as a manager, it’s possible to create a work environment where introverts like me can feel comfortable giving their feedback. Having studied this issue and gathered insights from thousands of employees we work with through Know Your Company, I now know how to build a workplace where quieter employees feel comfortable speaking up.Here are 11 things you can do and say today to encourage even your most introverted employee to be more forthcoming with you…1. Set up a time to talk in advance.My former boss used to walk up to an employee’s desk and ask her, “How are things going?” or “Wanna go grab coffee in like 10 minutes and have a quick one-on-one?” While these seem like positive, well-intentioned gestures on the surface, I remember these “surprise” requests for feedback feeling abrupt and off-putting to me as an employee. As introverts, we prefer having time to reflect, process, and prepare what we might want to share. So asking for feedback on-the-spot without a heads up doesn’t jive. Instead, set up time to ask for feedback in advance. For example you might say: “Hey ____, I would love an opportunity to grab some time with you, and simply listen to what you think could be better in the company. Do you have any time later this week or next?”2. Be clear on the “why.”Are you asking for feedback to “check a box” because it’s something leaders are “supposed to do”? Or do you genuinely believe that your employees’ feedback is paramount to the business as a whole? If it’s the latter, make that clear. (And if it’s the former, here’s a bit of data to understand why employee feedback matters). Oftentimes, introverts like myself don’t speak up because it’s unclear why our feedback is being asked for and if it will be valued. If I don’t hear the “why,” then I’m not going to put in the extra energy to share feedback that already feels unnatural for me to share in the first place.As a manager, you must reveal the “why” behind you asking for feedback and show your employees why their feedback matters to you. For instance, you can say something like: “The reason I’m asking for your input is because I truly believe your suggestions will help the company get to where it needs to be in the long run.”3. Ask “what” instead of “any.”When you’re asking your quieter employees for feedback, pay attention to the exact words in the questions you’re asking. Using the word “what” instead of “any” invites a greater response to a question. For example, when you ask, “Do you have any feedback on how the last client meeting went?” it’s very easy for the person to default and say “no.” But when you ask, “What could have been better in that last client meeting?” that question assumes that there are things that could be better. Asking “what” instead of “any” opens the opportunity for someone to provide a more honest answer.4. “Time box” your question.Give your employees a specific timeframe to contextualize their feedback the next time you ask them a question. This helps more introverted employees in particular think of feedback that is more concrete to share with you. I call this “time boxing.” For example, rather than asking, “What could we do better?” which usually leads to generic, vague responses, I’ll ask, “What’s something in the past 2 weeks that we could’ve done better? ” When you narrow that frame of reference to “the past two weeks,” it’s much easier for the other person to respond. She or he is now reflecting on just the past two weeks, instead of having to jog their memory for the past year or more.5. Ask about “one thing.”Not only do I try to contextualize questions to specific timeframe, but I try to ask about one thing, instead of leaving a question open-ended. For example, instead of asking, “What could we have improved on in that last project?” you should ask, “What one thing in the last project could we have improved on?” By asking for “one thing,” you make the question much less overwhelming for an introverted employee to answer. And the less overwhelming the question is, the more likely it is that you’ll get a candid, in-depth answer.6. Ask: “I feel X didn’t go well. Would you agree or disagree?”Another way to create a safe environment for your quieter employees to open up is to admit something you’re struggling with yourself. This is particularly helpful when you’re noticing radio silence from an employee. For example, let’s say you ask her or him, “What’s one thing about the last project we could have improved on?” And the other person is clamming up and can’t seem to think of anything (even though you asked about “one thing”). Try sharing something you think didn’t go well, and say: “I feel like I personally didn’t do the best job at X. Would you agree or disagree?” This vulnerability gives permission to the other person to be critical about something they might not otherwise be.7. Look to the future.People tend to be more honest when you ask them about the future versus the past. This is because giving feedback about the past can feel like a negative critique about what went wrong, while giving feedback about the future is seen as a forward-looking, creative opportunity to make things better. Use this to your advantage when looking to get honest feedback from a quieter employee. For example, you can ask: “Going forward, what’s one thing you think we should try doing as a company to improve our marketing?” instead of “What should we have done to prevent the marketing initiative from failing in the past?” See how the first question about the future seems much more positive and inviting compared to the second question about the past.8. Bring a notebook.Whether or not you consider yourself an avid notetaker, bring a notebook to your next one-on-one with your employee. When you have a notebook in front of you and a pen in hand, you’re indicating that you’re ready to listen, absorb, and take notes on the feedback the other person is giving you. It also demonstrates that you’re not entering the conversation with an already fixed agenda of what you want to get out of it. For an introverted employee, this is especially important, as it reassures her or him that the energy it will take to open up and give honest feedback will be worth it.9. Say thank you.Showing gratitude to an employee who shares a dissenting point-of-view is one of the most effective ways to encourage her or him to be honest with you... Yet it’s something we often forget to do. Get into the habit of regularly saying, “I appreciate that viewpoint” or “It means a lot to hear that” or simply “Thank you” every time an employee gives you feedback. When you do, you prove that candid feedback is welcome — even if it’s an opinion you might not outright agree with. Your quieter employees will be more willing to open up again the next time around, if you show gratitude for their input.10. Be quiet.Perhaps the best way to show someone who is more introverted that you want to listen to their feedback is to do that: Just listen. Be quiet. Don’t rebut. Don’t talk. Don’t think about what you’re going to say next. Just listen. Why? Typically, when you respond right away, you come across as defensive. And when you come across as defensive, it means you didn’t really want to hear the feedback. This will discourage an employee from sharing their honest feedback with you in the future.If you do feel like you need respond, you can say something like: “I’m grateful to you for sharing that. Let me take some time to digest what you’re saying and get back to you.” Introverts in particular recognize silence not as an absence of thought, but as a space for deep thinking. They’ll respect that you’re not trying to counter every point or talk over them.11. Let her or him know WHEN you’ll follow up — and stick to it.The biggest reason why quieter employees tend to not be as forthcoming with feedback is because of the sense of futility: It feels futile to give feedback because no action will be taken. In fact, studies have shown that futility is the #1 reason employees don’t speak up at work. This means in order to overcome the sense of futility and get honest feedback from your employees, you must communicate how you’re going to close the loop about a piece of feedback that’s been given to you.To do this, the next time an employee shares some salient feedback with you, try saying: “This is a helpful piece of feedback. I’m going to chew on it and get back to you by next Friday on how we’ll more forward.” Or if it’s something you can take action on immediately, you can say: “Because you shared this, I’m going to change X for our next project.” If it’s something that requires some time to think through, you can say: “Can we follow-up 2 weeks from now, and I’ll have an update on where I think we should go from here?” Notice in each of these examples, I’m very specific about when I will follow up with the employee. Make sure you do the same to show you’re serious about acting on their feedback or resolving their issue in some way.These tactics are helpful not only in asking for feedback from introverted employees, per se. As managers, founders, and CEOs, we all have at least one employee who we wish we heard from more regularly: An employee in our company who we might not see all the time, an employee who we haven’t developed a strong personal rapport with yet, or an employee who we’re dying to know what she or he really thinks.The next time you’re wanting to get honest feedback from one of these employees, try a few of these 11 suggestions. It’s as simple as setting up a time to talk in advance or bringing a notebook (or both!).You’ll hear more honest feedback from your employee than ever before.If you’re looking for a way to continually get feedback from the employees you don’t typically hear from, take a look at Know Your Company. We built Know Your Company with the sole purpose of creating a safe environment for quieter employees to speak up at work. Sign up for a free trial of Know Your Company today.Lastly, if you found this post useful, please feel free to share + give it ❤️ so others can find it too! Thanks 😊 (And please say hi at @cjlew23!)11 ways to get feedback from your most introverted employee was originally published in Signal v. Noise on Medium, where people are continuing the conversation by highlighting and responding to this story.


Source: 37signals