Archives

Category: TIL

  • Powering a Blog Through Medium


    Medium is a wonderful network and editorial platform that allows anyone to share their stories and their ideas. It allows those who don’t want to run a full blog to publish their content easily. If you do want to start a full blog without the overhead of buying hosting or running a WordPress site, it is super easy to power your blog with a custom domainArchived Link through Medium. This means you can buy your own site name (i.e. http://amandagrimmett.com) and the Medium publication will show up there. Here is what you need to do:

    1. Sign up for a Medium account.
    2. Set up a new publicationArchived Link on Medium. Think of this as your blog.
    3. Fill out this form with your publication’s URL and what custom domain you want to point to it.
    4. Medium will respond within 12 hours with a list of A records and a CNAME record that you’ll need to add to your registrar. Here is a cheatsheet for you and Medium’s support pageArchived Link. This is a bit of a pain, but thankfully a good registrar like Hover can do this for you. Hover’s new Connect service presets the 12 A records for you and steps you through setting a CNAME record.
    5. Within a few hours your domain will point to your Medium publication and your blog will be live!

    I just set one up this weekend for my wife’s awesome new blog. I couldn’t believe how simple it was. My only frustration was the wait time between submitting the request and getting a response from Medium. It was a weekend though, so I can’t complain.

    Once it is set up, you log in through Medium and publish to it through the Medium platform. It has the benefit of being tied in to the entire Medium ecosystem, so sharing, commenting, and gaining followers is easy. You are constrained to the limits of their platform, but it does have some customization optionsArchived Link. This is perfect for those of you focusing on your writing.

    Go forth and write!

    Medium gif

  • How to Pre-fill Google Forms


    Did you know that you can pre-fill Google Forms based on a URL? Did you also know that if you have a basic example you can automate it with a database and send personalized forms?

    General Instructions

    1. On your form, click the vertical ellipses in the upper right corner to reveal the menu.
    2. Click “Get pre-filled link” Get pre-filled link
    3. This shoots you over to a page where you can fill in the fields you want pre-filled and it will generate a URL for you. Copy this. If you want the link you send out to everyone to have the same pre-filled info, your work is done. Send the link out!

    How you might automate it

    Here is my example form.Once I went through the above steps to get a pre-filled link, here is what I got:

    https://docs.google.com/forms/d/1nuxTbnUGkTaZw1uGIXXgqfx3hYRi3ZaPahdwDNEgy3Q/viewform?entry.2005620554=Chuck&entry.1045781291=cagrimmett@gmail.com&entry.1065046570=308+Negra+Arroyo+Lane,+Albuquerque,+NM+87114&entry.1166974658=281-330-8004&entry.839337160=This+is+so+useful!

    The stuff we are looking for is between the “=” and “&” signs:

    https://docs.google.com/forms/d/1nuxTbnUGkTaZw1uGIXXgqfx3hYRi3ZaPahdwDNEgy3Q/viewform?entry.2005620554=Chuck&entry.1045781291=cagrimmett@gmail.com&entry.1065046570=308+Negra+Arroyo+Lane,+Albuquerque,+NM+87114&entry.1166974658=281-330-8004&entry.839337160=This+is+so+useful!

    As you can see, those are the items I filled in on my own. How might one automate this, then? Here are a few suggestions:

    1. Using handlebars or Liquid, fill them in with the appropriate expressions and use the appropriate objects to fill them in: https://docs.google.com/forms/d/1nuxTbnUGkTaZw1uGIXXgqfx3hYRi3ZaPahdwDNEgy3Q/viewform?entry.2005620554={{name}}&entry.1045781291={{email}}&entry.1065046570={{address}}&entry.1166974658={{phone}}&entry.839337160={{comments}}
    2. Using PHP, you might assign variables for these items and fill them in from your database with a select statement: https://docs.google.com/forms/d/1nuxTbnUGkTaZw1uGIXXgqfx3hYRi3ZaPahdwDNEgy3Q/viewform?entry.2005620554=$name&entry.1045781291=$email&entry.1065046570=$address&entry.1166974658=$phone&entry.839337160=$comments
    3. On Mailchimp, you’d have to use Merge Tags: https://docs.google.com/forms/d/1nuxTbnUGkTaZw1uGIXXgqfx3hYRi3ZaPahdwDNEgy3Q/viewform?entry.2005620554=*|name|*&entry.1045781291=*|email|*&entry.1065046570=*|address|*&entry.1166974658=*|phone|*&entry.839337160=*|comments|*

    Note: Make sure you are URL encoding things with weird characters and spaces like an address. Example: 308+Negra+Arroyo+Lane,+Albuquerque,+NM+87114 vs 308 Negra Arroyo Lane, Albuquerque, NM 87114

    Completion of the code in 1, 2, and 3 is an instruction left to the reader. I’m assuming that if you want to do something like generate thousands of personalized URLs to a form, you probably already have a database and know how to use it. I hope this guides you in the right direction! Email meArchived Link if you want some help.

  • Take Notes During Your Next Crisis


    The best time to learn how to handle crises is right after you just had one. Analyze what happened, what you did to solve it, and then make a checklist for when it happens next time.

    Then, during your next crisis, take a deep breath and start jotting down notes of what is happening and what you are doing. This makes your post-game analysis easier to complete.

    We had a mini crisis at work today: Some of our sites weren’t connecting to our MySQL servers and we couldn’t figure out why. The problem was intermittent. Our sysadmin is on vacation, so a few of us had to jump in and try to figure it out. When we were in a holding pattern while waiting for our hosting provider to call us back, I made a checklist of what we need to do the next time this happens. This took into account what we had to figure out on the spot, look up, diagnose, and what we learned. Now we have it for the future.

    H/T to Episode 279 of Back to Work with Merlin Mann and Dan Benjamin. I listened to it this morning and their discussion of making checklists while traveling for travel made me think of doing this during the MySQL issues.

  • jQuery’s Greater and Less Than Selectors


    Today I learned about jQuery’s greater than selector, :gt().

    The :gt() selector selects elements with an index number higher than a specified number. The index numbers start at 0.

    I’m using this selector to hide more than 10 recent posts on my TIL page. Each of these posts is inside a list item (li) and a child of the unordered list (ul) with a class called “recent”. I then added a toggle function on a button to show them again by using the same selector.

    Example:

    $(document).ready(function(){   $("ul.recent").find("li:gt(9)").hide();   $("ul.recent").has("li:nth-child(10)").after(""showhide button" >Show/Hide all TIL posts");   $("a.showhide").click(function() {     $("ul.recent").find("li:gt(9)").toggle("slow");     $   }); }); 

    There is also a less than selector in jQuery. It works similarly, except that it selects elements with an index number lower than the specified number. Syntax: :lt()

    Note: Since I’m using a Jekyll site and generating that TIL list via Liquid, another solution is to set a counter after which it automatically adds a class directly to the HTML to hide those items that we can then toggle later. I think the jQuery solution is cleaner, though.

  • Scheduling Jobs with Cron


    Cron is a time-based job scheduler in Linux and Unix.

    Basic usage

    Read the manual:

    $ man crontab

    Load your personal crontab (cron table) file:

    $ crontab -e

    View your personal crontab:

    $ crontab -l

    Syntax:

    min hour day of month month day of week command
    * * * * * command
    0 – 59 0 – 23 1 – 31 1 – 12 0 – 6 (0 to 6 are Sunday to Saturday) shell command you want to run at that time

    Intervals:

    • */N is the syntax for an interval
    • You can use commas if the interval is irregular

    Time Syntax Examples

    Example Explanation
    0 9 * * 1-5 Run Monday through Friday at 9am server time
    0 15 * * * Run every day at 3pm server time
    30 */2 * * * Run every 2 hours, on the half hour
    0 0 1 1 * Run once a year at midnight on January 1
    0 3,7,12,18 * * * Run daily at 3am, 7am, noon, and 6pm

    Full example

    Download a JSON file from Quandl and overwrite GOLD.json with it Monday through Friday at 5pm server time:

    0 17 * * 1-5 wget -O "/path/to/quandl_data/GOLD.json" "https://www.quandl.com/api/v3/datasets/LBMA/GOLD.json"

    Things to look out for

    • Surround anything with possible odd characters or spaces with quotes: URLs, local file paths, etc. This will keep you from getting errors.
    • Use a full file path from root instead of ~/ – Tildes aren’t interpreted the same way as on the command line
    • Times are always in server time. If you don’t know what time it is on the server, run: $ date

    Thanks goes out to Eric Davis for helping me out with this!

  • DNS Terms Cheatsheet


    There are a lot more domain record terms than this, but these are the ones I most frequently encounter. I’ll add more as I run into them.

    Records

    Record Description Use
    A Address Record Point a domain to a specific IP address
    CNAME Canonical Name Record This is an alias of one name to another. Example: A CNAME of www.cagrimmett.com to cagrimmett.com tells the server to look for the WWW version wherever the non-WWW version’s A record resides. CNAME records must always point to another domain name, never directly to an IP address.
    MX Mail Exchange Record Direct’s a domain’s email to the server hosting the accounts. I most frequently use this for setting up Google Apps.
    NS Name Server Record Determines which servers will communicate DNS info for a domain. You usually have a primary and secondary. This site uses Cloudflare’s name servers.
    DNSKEY DNS Key Record The key record used in DNSSEC.

    Terms

    Term Full Name Description
    DNS Domain Name System Basically a phone book for the web. When you try to go to a domain (e.g. google.com) in your browser, the domain name system tells your browser where to find that domain, usually an IP address.
    TTL Time To Live How long it will take any change you make now to go into effect
    @ Self-referential character You don’t use the actual domain name in your DNS settings. Instead, you use the @ symbol to indicate the domain name.
    DNSSEC Domain Name System Security Extensions A set of protocols that add a layer of security to the DNS lookup and exchange processes.
  • Automating Blog Posts with AppleScript v2


    I decided that my earlier attempt at automating creation of these TIL posts was still too manual. This one is better!

    What it does:

    1. Creates a markdown file with today’s date and my specified string at the title.
    2. Launches Coda in the foreground and goes to my preset Jekyll blog site (cagrimmett.com)
    3. Opens the file it just created in the front window.
    4. Expands the previous TextExpander snippet I created as a template for these TIL posts.

    The Script

    %filltop%  on textexpander(abbreviation)	 do shell script "cd ~/Projects/cagrimmett-com/_posts/til; touch %snippet:fname%"  tell application "Coda 2" 	activate window 	get site 6 of application "Coda 2" 	tell window 1 		connect to site 6 of application "Coda 2" 			open "Macintosh HD:Users:CAG:Projects:cagrimmett-com:_posts:til:%snippet:fname%" 	end tell end tell tell application "TextExpander" 	expand abbreviation ";tem" end tell  end textexpander

    Here are what the other two snippets fill in:

    fname– I had to make this a snippet instead of putting it in directly because in the filename creation string TextExpander didn’t recognize the date string. If you nest it inside another snippet, it works just fine.

    %Y-%m-%d-%filltext:name=title%.md 

    ;tem

    --- layout: post title:  author: Chuck Grimmett date: %Y-%m-%d category: TIL feature-img: "/img/defaults/%snippet:;random-img%" tags:  -  excerpt: 

    --- ### Today I learned:
  • Many-to-Many Relationships in Relational Data Models


    Today I learned about Many-to-Many relationships in relational data models

    • A many-to-many relationship is a type of cardinality that refers to the relationship between two entities A and B in which A may contain a parent instance for which there are many children in B and vice versa.
    • For example, a recipe may have many ingredients and a specific ingredient may be used in many recipes.
    • In SQL, this relationship is handled by an associative table. The primary key for this type of table is composed of two different columns, both of which reference columns in other tables that you are associating together.
    • It is a good convention to name these associative tables by how they reference each other: Table1_Table2
    • A SELECT statement on an associative table usually involves JOINing the main table with the associative table.
    • If you don’t want to run these JOINs every time, create a view first. A view is a virtual table based on the result-set of an SQL statement. You can add SQL functions, WHERE, and JOIN statements to a view and present the data as if the data were coming from one single table.

    Example SQL for creating an associative table:

    CREATE TABLE recipes_ingredients (     RecipeID INT(11) REFERENCES Recipes (RecipeID),     IngredientID INT(11) REFERENCES Ingredients (IngredientID),     PRIMARY KEY (RecipeID, IngredientID) )
  • Trifacta Wrangler


    Trifacta Wrangler

    Trifacta Wrangler is a free program (currently in beta) that helps you clean up data sets and gives you a first cut at basic analysis. It is great for quickly turning messy data into structured, manageable formats.

    Trifacta Wrangler

    In the past few days I’ve used it to analyze huge log files and turn messy JSON in structured CSVs that I could import into SQL.

    Quick tips:

    • splitrows always has to come first. The program usually tries to split by \n (new line) first, but that doesn’t always work for JSON. Try splitting by something like },{, or do a quick find and replace ( },{ for }|||{ ) and do the split by ||| if you want to keep the curly brackets for an unnest.
    • unnest is very powerful for splitting out JSON values out into separate columns titled by their keys.
    • flatten works better than unnest in cases where the JSON does not have keys. It creates new rows and repeats other values in adjacent columns to keep the relation. This works well if you have an ID column and are going to eventually stuff things into a relational database.

    Here is documentation for the Transforms.

  • Linux Webserver Cheat Sheet


    There is obviously a lot more than this, so I’ll add more as I encounter and use them.

    Last updated on: July 6, 2016.

    Connecting

    • Connecting: ssh username@serveraddress
    • Switching to root once you get in, if needed: su root

    Where to find things

    • Logs: /var/log/apache2
    • Site roots: /var/www/ if a single site, /var/www/vhosts if multiple sites on virtual host infrastructure
    • Config files: /etc/apache2/sitesavailable and /etc/apache2/sitesenabled

    Changing directories, creating files and directories, viewing text files

    • Changing directory: cd ~/path/to/folder
    • Going up one directory: cd ..
    • Creating files: touch filename.jpg
    • Creating directories: mkdir directory_name
    • Viewing text files: less filename.txt or cat filename.txt
    • Closing out of less: q

    wget

    Wget retrieves content from web servers.

    Syntax:

    $ wget [option] URL

    Options:

    • -O – Which output file the file you are downloading should get written to.
    • -q – Quiet mode. Doesn’t show the download status and other output.

    Example – Getting a file from the web:

    $ wget -O /path/to/filename.json http://example.com/URL/to/filename.json

    Reading the manual for commands

    $ man <command>

    Examples:

    • man cron
    • man date

    Crontab

    Cron is a time-based job scheduler in Linux and Unix. Here is my full TIL on cron.

    Load your personal crontab (cron table) file:

    $ crontab -e

    View your personal crontab:

    $ crontab -l

    Syntax:

    min hour day of month month day of week command
    * * * * * command
    0 – 59 0 – 23 1 – 31 1 – 12 0 – 6 (0 to 6 are Sunday to Saturday) shell command you want to run at that time

    Examble: Download a JSON file from Quandl and overwrite GOLD.json with it Monday through Friday at 5pm server time

    0 17 * * 1-5 wget -O "/path/to/quandl_data/GOLD.json" "https://www.quandl.com/api/v3/datasets/LBMA/GOLD.json"

    Date

    Display a date and time:

    • $ date spits out the date and time on the server
    • $ TZ=US/Pacific date spits out the server’s date and time adjusted to the Pacific timezone
    • TZ=US/Eastern date -d 'Tue Jul 5 10:43:07 PDT 2016' converts the timestamp in the -d option to the Eastern timezone.
    • date -d @1467740657 converts UNIX timestamps to something you can actually read

    Eric Davis has a Simple CLI date calculator writeup on his site.

  • Cleaning up your Mac with Hazel


    Today I learned how to clean up my Mac with Hazel

    Hazel

    Hazel is a preference pane-based application that helps you automate organization on your Mac.

    Within a few hours of using Hazel, I was able to clean out my 1000+ file Downloads folder, tame my unruly Desktop, get rid of all the trash that accumulated in my home folder, and organize my stashes of client files. I also set up rules for the future that will keep these places neat and orderly.

    The next step is to turn my gaze on my photo library to create one master repository.

    Tools and guides

    The best tools/guides on Hazel right now are:

  • Using Word Frequency Charts for Better Word Clouds


    Word clouds

    Data scientists notoriously hate word clouds. Besides for figuring out what the top 2-3 words are (because they are the biggest), it is difficult to see how much one word is used relative to another. Unfortunately, clients and non-data people love word clouds and sometimes insist on them. What is a self-respecting data nerd to do?

    Pair it a word frequency chart!

    The easiest way to do this is by using Python’s counter:

    Counter(words).most_common()

    Then you can use your favorite charting tool to make a bar chart of the results. I prefer D3.js.

    Results

    Word Frequency Chart

    Word Cloud

    If you see both together, you get a better understanding of the words being used. Of course, a single word doesn’t always capture sentiment. They can be helpful in smaller data sets, but sometimes common phrases are more helpful in larger data sets. For common phrases, use n-gram analysis.

    For more on visualizing text, check out episode 62 of the Data Stories podcast and the Text Visualization Browser.

  • Ember.js Basics


    Resources

    What it is

    • Ember.js is an open-source Javascript framework with the Model–view–viewmodel pattern.
    • Ember is an opinionated framework. This means that most architectural design decisions have been made for you by the developers of the framework. The advantage of this is that anyone who knows Ember can load your code and understand within a few minutes what is going on.

    Core Concepts

    Ember Core Concepts

    1. Ember router maps a URL to a route handler
    2. Route handler renders a template, then a model that is available to the template. Templates use Handlebars syntax.
    3. Models save data in a “persistent state”, which is fancy language for putting it in a database or data store of some kind.
    4. Components control how the UI behaves. They have two parts: A handlebars template and a javascript source file that defines the behavior.

    Installing and running a project

    Install:

    $ npm install -g ember-cli@2.6

    Creating a new app:

    $ ember new 

    Starting the development server. You must be in the project folder (cd to it). It will serve on http://localhost:4200/

    $ ember server

    or ember s for short

  • Link Posts with Jekyll


    Today I learned:

    How to make link posts (or external post links) with Jekyll

    I’m an avid reader of Marco.org and Daring Fireball. They both have these nifty posts that link directly to external pages. Now that I have two blogs, a podcast, and occasionally have work published elsewhere, I want to keep a record of these things on my cagrimmett.com blog. Link posts are an excellent way to do that.

    Jekyll doesn’t have this capability out of the box, but with a little Liquid magic you can make it happen. I followed Christian-Frederik Voigt’s method. This takes advantage of the ability to create items in each post’s YAML front-matter and use them in template files:

    First, create a variable called link: in your YAML front-matter for the post you want to link elsewhere. Fill it in with the link you want to post to link to:

    --- layout: post title: Snack Time Episode 3 - Negroni Week date: 2016-06-09 feature-img: "/img/snacktime-pattern.png" excerpt: blah blah blah excerpt here link: http://snacktime.fm/episodes/2016/6/9/episode-3-negroni-week ---

    Then anywhere in the template files that generates post headlines, you’ll need to write a conditional to check if the new link: variable is present. If it is, you’ll want to write the headline’s link there (post.link) instead of the post.url. I also added an arrow inside another conditional to specify that this is an outgoing link:

    You’ll want to use this anywhere your posts are listed. For me that is in index.html and 404.html.

    See this in action on my homepage.

    This method works great if you want to have direct links to other places and don’t want a permalink page on your own site that you can reference. I don’t care about that, so I didn’t make this for my site. If you want that, you’ll need to refer to Christian-Frederik Voigt’s method. The short story is that you’ll also need to modify the post and page templates.

  • Counting and JSON output in Jekyll


    Today I learned:

    Jekyll has a built-in filter called jsonify to turn data into JSON and Liquid has a filter called json. These is pretty useful for turning standard Jekyll output arrays into JSON, but if you need something more customized, you’ll need to build it yourself because Liquid’s ability to create arrays is limited. (Not a criticism, just an observation. It is primarily a templating language.)

    For example, I wanted to store the dates I have blog posts for and the number of posts on each of those dates in order to pass it to a heatmap calendar. I couldn’t figure out a way to build that into a single array with Liquid (and I didn’t want to drop down into Ruby), so I figured out a way build JSON manually.

    This outputs my blog’s post dates in the Unix timestamp format and displays the number of posts on a given day and leaves the trailing comma off to comply with the JSON standard:

    {% assign counter = 0 %}{ {% for post in site.posts %}{% capture day %}{{ post.date | date: '%s' }}{% endcapture %}{% capture prevday %}{{ post.previous.date | date: '%s' }}{% endcapture %}{% assign counter = counter | plus: 1 %}{% if day != prevday %}"{{ post.date | date: '%s' }}": {{ counter }}{% assign counter = 0 %}{% if forloop.last == false %},{% endif %} {% endif %}{% endfor %}} 

    Output:

    { 	"1463716800": 1, 	"1463371200": 1, 	"1462593600": 2, 	"1462507200": 1, 	"1462248000": 1, 	"1462161600": 1, 	"1461643200": 1, 	"1461297600": 3, 	"1461124800": 1, 	"1460952000": 1, 	"1460606400": 1, 	"1460347200": 1, 	"1459915200": 1, 	"1458792000": 1, 	"1458705600": 1, 	"1458619200": 1, 	"1458532800": 1, 	"1458446400": 1, 	"1458273600": 2, 	"1457586000": 1, 	"1457499600": 1, 	"1457413200": 1, 	"1457326800": 1, 	"1457240400": 1, 	"1456981200": 1, 	"1456894800": 1, 	"1456808400": 1, 	"1456722000": 1, 	"1456376400": 1, 	"1456290000": 1, 	"1456203600": 1, 	"1456117200": 1, 	"1456030800": 1, 	"1455944400": 1, 	"1455858000": 1, 	"1455771600": 2, 	"1455685200": 1, 	"1455598800": 1, 	"1455512400": 1, 	"1455339600": 1, 	"1455253200": 1, 	"1455166800": 1, 	"1455080400": 1, 	"1449032400": 1, 	"1441944000": 1, 	"1441598400": 1, 	"1441166400": 1, 	"1416978000": 1, 	"1377662400": 1 }

    The key to getting the counts right was advancing the counter by 1, comparing the date of the current post to the previous post, outputting the date and the count if they did not match, and if they did match advancing the counter by 1 and comparing again. After the date and count are output, the counter is reset to 0.

    You can see the heatmap calendar on my TIL page.


    Thanks to Eric Davis for advice on using less code and pointing out how I can make the trailing comma if statement more efficient: %if forloop.last == false% , %endif%

    He said:

    If there’s one bit of “programming advice” I would give to anybody mucking around with this stuff, it’s this: Write as little code as humanly possible. The most bug-free line of code is the line not written.

    What I need to ask myself: If something is being repeated, why?

  • Identifying and killing background Python processes


    Today I learned:

    How to kill rogue background Python processes

    I started a simple Python http server as a background process at the end of a Python script. That was a mistake. Since it isn’t running in an open Terminal window, it is easy to forget about and it will run until you kill the process or reboot your machine. I don’t like that.

    Here is how to identify and kill background Python processes in Terminal.app:

    Run:

    ps -elf | grep python

    This will return something like this:

    501 74440 74439     4004   0  31  0  2505592   9276 -      S        0 ??         0:00.29 python -m Simple  8:43AM 501 77045 77016     4006   0  31  0  2436888    812 -      S+       0 ttys000    0:00.00 grep python       8:57AM

    The second column is the PID. Make note of which one you want to kill. Then run:

    kill -9 PID

    Example. Let’s say I want to kill the python -m SimpleHTTPServer process. I see that its PID is 74440. So I would run:

    kill -9 74440
  • Simple HTTP server with Python


    Today I learned:

    Simple HTTP server

    Need to quickly test how something works/functions but don’t want to go through the hassle of putting it on a webserver? Python has you covered.

    $ cd /home/some/dir $ python -m SimpleHTTPServer

    Then you can access the stuff in that directory it via http://localhost:8000/ in your browser!

    Control + c stops it.

  • Regex Lookahead and Lookbehind


    Today I learned:

    Regex Lookahead and Lookbehind

    (?=foo) Lookahead Asserts that what immediately follows the current position in the string is foo
    (?<=foo) Lookbehind Asserts that what immediately precedes the current position in the string is foo
    (?!foo) Negative Lookahead Asserts that what immediately follows the current position in the string is not foo
    (? Negative Lookbehind Asserts that what immediately precedes the current position in the string is not foo

    Example

    Expression: Hell(?=o) - Lookahead

    • Hellish fails
    • Hello passes
    • MelloHello passes

    Use

    The basic example above isn’t very helpful. But it can be very useful for password rule validation or advanced find/replaces.

  • Responsive D3.js bar chart with labels


    Hey, so this post is broken. I moved platforms and some of my old tutorials don’t play nicely with WordPress. I’m working on fixing them, but in the meantime you can view the old version here: https://cagrimmett-jekyll.s3.amazonaws.com/til/2016/04/26/responsive-d3-bar-chart.html

    Today I learned some cool stuff with D3.js!

    Here is a minimalist responsive bar chart with quantity labels at the top of each bar and text wrapping of the food labels. It is actually responsive, it doesn’t merely scale the SVG proportionally, it keeps a fixed height and dynamically changes the width.

    For simplicity I took the left scale off. All bars are proportional and are labeled anyway.

    Go ahead and resize your window! This has a minimum width of about 530px because of the text labels. Any smaller than that and they are very difficult to read.

    The basic HTML

     id ="chartID"> 

    The Styles

    You’ll see that the axis is actually there but it is white. I found it useful to learn to draw it, but I didn’t want it so I am keeping it hidden.

    .axis path, .axis line {     fill: none;     stroke: #fff;   } .axis text {   	font-size: 13px;   } .bar {     fill: #8CD3DD;   } .bar:hover {     fill: #F56C4E;   } svg text.label {   fill:white;   font: 15px;     font-weight: 400;   text-anchor: middle; } #chartID { 	min-width: 531px; }

    The Data

    var data = [{"food":"Hotdogs","quantity":24},{"food":"Tacos","quantity":15},{"food":"Pizza","quantity":3},{"food":"Double Quarter Pounders with Cheese","quantity":2},{"food":"Omelets","quantity":30},{"food":"Falafel and Hummus","quantity":21},{"food":"Soylent","quantity":13}]

    The Javascript Heavy Lifting

    This is where D3 really comes in.

    1. Setting the margins, sizes, and figuring out the basic scale.
    2. Setting the axes
    3. Drawing the basic SVG container with the proper size and margins
    4. Scaling the axes
    5. Drawing the bars themselves
    var margin = {top:10, right:10, bottom:90, left:10};  var width = 960 - margin.left - margin.right;  var height = 500 - margin.top - margin.bottom;  var xScale = d3.scale.ordinal().rangeRoundBands([0, width], .03)  var yScale = d3.scale.linear()       .range([height, 0]);   var xAxis = d3.svg.axis() 		.scale(xScale) 		.orient("bottom");               var yAxis = d3.svg.axis() 		.scale(yScale) 		.orient("left");  var svgContainer = d3.select("#chartID").append("svg") 		.attr("width", width+margin.left + margin.right) 		.attr("height",height+margin.top + margin.bottom) 		.append("g").attr("class", "container") 		.attr("transform", "translate("+ margin.left +","+ margin.top +")");  xScale.domain(data.map(function(d) { return d.food; })); yScale.domain([0, d3.max(data, function(d) { return d.quantity; })]);   //xAxis. To put on the top, swap "(height)" with "-5" in the translate() statement. Then you'll have to change the margins above and the x,y attributes in the svgContainer.select('.x.axis') statement inside resize() below. var xAxis_g = svgContainer.append("g") 		.attr("class", "x axis") 		.attr("transform", "translate(0," + (height) + ")") 		.call(xAxis) 		.selectAll("text"); 			 // Uncomment this block if you want the y axis /*var yAxis_g = svgContainer.append("g") 		.attr("class", "y axis") 		.call(yAxis) 		.append("text") 		.attr("transform", "rotate(-90)") 		.attr("y", 6).attr("dy", ".71em") 		//.style("text-anchor", "end").text("Number of Applicatons");  */   	svgContainer.selectAll(".bar")   		.data(data)   		.enter()   		.append("rect")   		.attr("class", "bar")   		.attr("x", function(d) { return xScale(d.food); })   		.attr("width", xScale.rangeBand())   		.attr("y", function(d) { return yScale(d.quantity); })   		.attr("height", function(d) { return height - yScale(d.quantity); });

    Adding the quantity labels to the top of each bar

    This took me a while to figure out because I was originally appending to the rect element. According to the SVG specs this is illegal, so I moved on to appending them after everything else to they’d show on top. The positioning is tricky, too. I eventually found the correct variables to position it close to center. Then text-anchor: middle; sealed the deal.

    // Controls the text labels at the top of each bar. Partially repeated in the resize() function below for responsiveness. 	svgContainer.selectAll(".text")  		 	  .data(data) 	  .enter() 	  .append("text") 	  .attr("class","label") 	  .attr("x", (function(d) { return xScale(d.food) + xScale.rangeBand() / 2 ; }  )) 	  .attr("y", function(d) { return yScale(d.quantity) + 1; }) 	  .attr("dy", ".75em") 	  .text(function(d) { return d.quantity; });   	  

    Responsiveness

    The general method for making D3 charts responsive is to scale the SVG down proportionally as the window gets smaller by manipulating the viewBox and preserveAspectRatio attributes. But after digging around on Github for a while, I found a fancier solution that preserves the height and redraws the SVG as the width shrinks.

    document.addEventListener("DOMContentLoaded", resize); d3.select(window).on('resize', resize);   function resize() { 	console.log('----resize function----');   // update width   width = parseInt(d3.select('#chartID').style('width'), 10);   width = width - margin.left - margin.right;    height = parseInt(d3.select("#chartID").style("height"));   height = height - margin.top - margin.bottom; 	console.log('----resiz width----'+width); 	console.log('----resiz height----'+height);   // resize the chart        xScale.range([0, width]);     xScale.rangeRoundBands([0, width], .03);     yScale.range([height, 0]);      yAxis.ticks(Math.max(height/50, 2));     xAxis.ticks(Math.max(width/50, 2));      d3.select(svgContainer.node().parentNode)         .style('width', (width + margin.left + margin.right) + 'px');      svgContainer.selectAll('.bar')     	.attr("x", function(d) { return xScale(d.food); })       .attr("width", xScale.rangeBand());           svgContainer.selectAll("text")  		 	 // .attr("x", function(d) { return xScale(d.food); }) 	 .attr("x", (function(d) { return xScale(d.food	) + xScale.rangeBand() / 2 ; }  ))       .attr("y", function(d) { return yScale(d.quantity) + 1; })       .attr("dy", ".75em");   	            svgContainer.select('.x.axis').call(xAxis.orient('bottom')).selectAll("text").attr("y",10).call(wrap, xScale.rangeBand());     // Swap the version below for the one above to disable rotating the titles     // svgContainer.select('.x.axis').call(xAxis.orient('top')).selectAll("text").attr("x",55).attr("y",-25);     	     }

    Wrapping text labels

    Wrapping text labels is tricky. The best solution I found is the one Mike Bostock (D3’s creator) describes. I modified it slightly to work with my chart, but the overall solution is the same.

    function wrap(text, width) {   text.each(function() {     var text = d3.select(this),         words = text.text().split(/s+/).reverse(),         word,         line = [],         lineNumber = 0,         lineHeight = 1.1, // ems         y = text.attr("y"),         dy = parseFloat(text.attr("dy")),         tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");     while (word = words.pop()) {       line.push(word);       tspan.text(line.join(" "));       if (tspan.node().getComputedTextLength() > width) {         line.pop();         tspan.text(line.join(" "));         line = [word];         tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);       }     }   }); }

  • Strange issues with stacking on iOS Safari


    I was asked to help out with some CSS animation. I thought it would be no problem. A few hours at most. Boy was I wrong.

    The biggest issue I ran into was that an element that came in from the right (outside the boundaries was hidden by overflow) functioned just fine on desktop Safari and Chrome, but did not appear on iOS unless I ran the debugger and toggled overflow or display properties during the transition. position and z-index made no difference.

    I came across this article while researching solutions. The ending shed some light for me:

    ensure you don’t have a CSS property such as transform on the root element. It’s more than likely that this is creating a new stacking context for the enclosed elements.

    Whelp, that sucks. CSS animations rely almost solely on the transform property to operate. I played around a few more hours trying different techniques with containers and positioning. The only thing that sort of worked was using translate3d() and keeping the element active and in view the entire time. Then and only then did it work the same on iOS and desktop. If it paused or was hidden even for a moment, I couldn’t get it to come back.

    Safari on iOS is doing something funky with stacking when the transform property is involved and I couldn’t find any solid documentation on what is happening in order to come up with a good solution.

    If anyone has faced this beast and won, please drop me a line! I can’t post the specific elements here because it was client work, but I’m happy to discuss it one-on-one.


    Side note: Webkit is finally moving off of prefixes!