On this page:

Beginner web dev tip: Use Inspect Element to learn more about HTML and CSS on a page

One of the neat things about learning web development is that the Web is full of examples you can learn from. You can use your browser’s View Page Source or View Source command (available from the right-click menu in Google Chrome, Mozilla Firefox, or Internet Explorer). An even better way to explore, though, is to use the Inspect Element action from the right-click menu in those browsers. If you right-click on the item you want to learn more about, you’ll be able to see the HTML and the current CSS applied to the page.

I use Google Chrome most of the time, so I’ll show you Inspect Element screen in that browser. Here we’re looking at the button on CSS-Tricks.com:

2014-11-10 15_37_06-CSS-Tricks.png

You’ll see the HTML on the left side and the CSS on the right. You can check or uncheck different CSS rules, and you can double-click on things to edit them. Check out the right-click menus for even more options.

Sometimes you may need to click on a different element in order to see the CSS rules that are relevant to what you’re curious about. As you hover over different elements, you’ll see them highlighted on the page.

If you click on the Console tab, you can experiment with Javascript too. If you want to view both the inspect element information and the Javascript console at the same time, click on the icon that looks like a > on the right side. This is particularly handy if you have a large screen.

Hope that helps!

First steps towards Javascript testing

I know, I know, it’s about time I got the hang of this. Better late than never, right? =) Anyway, I spent some time going through tutorials for QUnit and Jasmine. For QUnit, I followed this Smashing Magazine tutorial on Javascript unit testing. I modified the code a little bit to add the Z timezone to the test data, since my tests initially didn’t pass.

test.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Refactored date examples</title>
    <link rel="stylesheet" href="http://code.jquery.com/qunit/qunit-1.15.0.css" />
    <script src="http://code.jquery.com/qunit/qunit-1.15.0.js"></script>
    <script src="prettydate.js"></script>
    <script>
     test("prettydate.format", function() {
       function date(then, expected) {
         equal(prettyDate.format('2008/01/28 22:25:00Z', then), expected);
       }
       date("2008/01/28 22:24:30Z", "just now");
       date("2008/01/28 22:23:30Z", "1 minute ago");
       date("2008/01/28 21:23:30Z", "1 hour ago");
       date("2008/01/27 22:23:30Z", "Yesterday");
       date("2008/01/26 22:23:30Z", "2 days ago");
       date("2007/01/26 22:23:30Z", undefined);
     });
     function domtest(name, now, first, second) {
       test(name, function() {
         var links = document.getElementById('qunit-fixture').getElementsByTagName('a');
         equal(links[0].innerHTML, 'January 28th, 2008');
         equal(links[2].innerHTML, 'January 27th, 2008');
         prettyDate.update(now);
         equal(links[0].innerHTML, first);
         equal(links[2].innerHTML, second);
       });
     }

     domtest("prettyDate.update", '2008-01-28T22:25:00Z', '2 hours ago', 'Yesterday');
     domtest("prettyDate.update, one day later", '2008-01-29T22:25:00Z', 'Yesterday', '2 days ago');
    </script>
</head>
<body>
  <div id="qunit"></div>
  <div id="qunit-fixture">
    <ul>
      <li class="entry" id="post57">
        <p>blah blah blah…</p>
        <small class="extra">
          Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-28T20:24:17Z">January 28th, 2008</a></span>
          by <span class="author"><a href="/john/">John Resig</a></span>
        </small>
      </li>
      <li class="entry" id="post57">
        <p>blah blah blah…</p>
        <small class="extra">
          Posted <span class="time"><a href="/2008/01/blah/57/" title="2008-01-27T22:24:17Z">January 27th, 2008</a></span>
          by <span class="author"><a href="/john/">John Resig</a></span>
        </small>
      </li>
    </ul>
  </div>
</body>
</html>

For practice, I converted the QUnit tests to Jasmine. The first part of the test was easy, but I wanted a clean way to do the HTML fixture-based tests for prettydate.update too. Jasmine-JQuery gives you a handy way to have HTML fixtures. Here’s what my code ended up as:

spec/DateSpec.js

describe("PrettyDate.format", function() {
    function checkDate(name, then, expected) {
        it(name, function() {
            expect(prettyDate.format('2008/01/28 22:25:00Z', then)).toEqual(expected);
        });
    }
    checkDate("should display recent times", '2008/01/28 22:24:30Z', 'just now');
    checkDate("should display times within a minute", '2008/01/28 22:23:30Z', '1 minute ago');
    checkDate("should display times within an hour", '2008/01/28 21:23:30Z', '1 hour ago');
    checkDate("should display times within a day", '2008/01/27 22:23:30Z', 'Yesterday');
    checkDate("should display times within two days", '2008/01/26 22:23:30Z', '2 days ago');
});
describe("PrettyDate.update", function() {
    function domtest(name, now, first, second) {
       it(name, function() {
           loadFixtures('test_fixture.html');
           var links = document.getElementById('qunit-fixture').getElementsByTagName('a');
           expect(links[0].innerHTML).toEqual('January 28th, 2008');
           expect(links[2].innerHTML).toEqual('January 27th, 2008');
           prettyDate.update(now);
           expect(links[0].innerHTML).toEqual(first);
           expect(links[2].innerHTML).toEqual(second);
       });
    }
    domtest("prettyDate.update", '2008-01-28T22:25:00Z', '2 hours ago', 'Yesterday');
    domtest("prettyDate.update, one day later", '2008-01-29T22:25:00Z', 'Yesterday', '2 days ago');
});

jasmine.html

<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Jasmine Spec Runner v2.0.2</title>

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.0.2/jasmine_favicon.png">
  <link rel="stylesheet" type="text/css" href="lib/jasmine-2.0.2/jasmine.css">

  <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.0.2/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.0.2/jasmine-html.js"></script>
  <script type="text/javascript" src="lib/jasmine-2.0.2/boot.js"></script>
  <script type="text/javascript" src="jasmine-jquery.js"></script>

  <!-- include source files here... -->
  <script type="text/javascript" src="prettydate.js"></script>

  <!-- include spec files here... -->
  <script type="text/javascript" src="spec/DateSpec.js"></script>

</head>

<body>
</body>
</html>

I’m looking forward to learning how to use Jasmine to test Angular applications, since behaviour-driven testing seems to be common practice there. Little steps! =)

Emacs: Limiting Magit status to a directory

I’m probably using Git the wrong way. In addition to the nice neat repositories I have for various projects, I also sometimes have a big grab-bag repository that has random stuff in it, just so that I can locally version-control individual files without fussing about with Emacs’ numbered version systems. Sometimes I even remember to organize those files into directories.

When you have a Git repository that’s not one logical project but many little prototypes, using Magit status to work across the entire project can sometimes mean running into lots of distracting work in progress. I wanted a way to limit the scope of Magit status to a specific directory.

Here’s the experimental code I came up with:

      (defvar sacha/magit-limit-to-directory nil "Limit magit status to a specific directory.")
      (defun sacha/magit-status-in-directory (directory)
        "Displays magit status limited to DIRECTORY.
Uses the current `default-directory', or prompts for a directory
if called with a prefix argument. Sets `sacha/magit-limit-to-directory'
so that it's still active even after you stage a change. Very experimental."
        (interactive (list (expand-file-name
                            (if current-prefix-arg
                                (read-directory-name "Directory: ")
                              default-directory))))
        (setq sacha/magit-limit-to-directory directory)
        (magit-status directory))

      (defadvice magit-insert-untracked-files (around sacha activate)
        (if sacha/magit-limit-to-directory
            (magit-with-section (section untracked 'untracked "Untracked files:" t)
              (let ((files (cl-mapcan
                            (lambda (f)
                              (when (eq (aref f 0) ??) (list f)))
                            (magit-git-lines
                             "status" "--porcelain" "--" sacha/magit-limit-to-directory))))
                (if (not files)
                    (setq section nil)
                  (dolist (file files)
                    (setq file (magit-decode-git-path (substring file 3)))
                    (magit-with-section (section file file)
                      (insert "\t" file "\n")))
                  (insert "\n"))))
          ad-do-it))

      (defadvice magit-insert-unstaged-changes (around sacha activate)
        (if sacha/magit-limit-to-directory
            (let ((magit-current-diff-range (cons 'index 'working))
                  (magit-diff-options (copy-sequence magit-diff-options)))
              (magit-git-insert-section (unstaged "Unstaged changes:")
                  #'magit-wash-raw-diffs
                "diff-files"
                "--" sacha/magit-limit-to-directory
                ))
          ad-do-it))

      (defadvice magit-insert-staged-changes (around sacha activate)
        "Limit to `sacha/magit-limit-to-directory' if specified."
        (if sacha/magit-limit-to-directory
            (let ((no-commit (not (magit-git-success "log" "-1" "HEAD"))))
              (when (or no-commit (magit-anything-staged-p))
                (let ((magit-current-diff-range (cons "HEAD" 'index))
                      (base (if no-commit
                                (magit-git-string "mktree")
                              "HEAD"))
                      (magit-diff-options (append '("--cached") magit-diff-options)))
                  (magit-git-insert-section (staged "Staged changes:")
                      (apply-partially #'magit-wash-raw-diffs t)
                    "diff-index" "--cached" base "--" sacha/magit-limit-to-directory))))
          ad-do-it)))

Now I can bind C-x v C-d to sacha/magit-status-in-directory and get something that lets me focus on one directory tree at a time. You can see my config in context at http://sachachua.com/dotemacs#magit

It feels like I’m probably trying to do things the Wrong Way and I should probably just break things out into separate repositories. Even though I realized this early on, though, I ended up digging into how to implement it just for the sheer heck of seeing if Emacs would let me do it. =) I don’t know how often I’ll use this function, but it was a good excuse to learn more about the way Magit works.

It took me an hour to find my way around magit.el, but that’s more my fault than the code’s. At first I tried to override magit-diff-options, but I eventually remembered that the paths need to come at the end of the command line arguments. (I had a cold! My brain was fuzzy!) It was fun poking around, though. Looking forward to learning even more about Magit!

My current book workflow

I pick up a lot of information from books. We have an amazing public library system, and I’m at the library at least once a week. Here’s my current workflow for reading and taking notes on books.

Finding books to read: I check the library’s new releases for interesting titles. I have a Ruby script that extracts the information and puts it into a text file. I delete anything that doesn’t interest me, and then I copy the IDs into another Ruby on Rails system that requests all the books for me. Sometimes I search for books by topic or get recommendations from other people.

Reading books: I’ve successfully weaned myself off the bad habit of folding over the corners of book pages (dogearing). Instead, I use book darts to point to the passages that I want to copy into my notes. I have only one tin of them, so that encourages me to harvest the notes from books before moving on to other books. If I don’t have my book darts handy, I use strips of paper, but they’re not as awesome.

I also keep paper and pen handy (index cards, or the small notebook I always have in my vest) so that I can take notes on ideas, questions, and other things that aren’t directly in the text of the book.

I mostly read non-fiction, so that’s easy to skim for interesting bits. I usually check the table of contents to get an overview of the book. I have no qualms about jumping straight to specific chapters and then wandering around a bit, or even picking just a few pages out of a book. I rarely use the index (and most books don’t have a good one anyway), although maybe I should check that more often.

Taking notes: I keep individual text files named after the title of the book (and sometimes authors as well). I usually include the ISBN so that I can easily look up a book later. The text files contain quotes, ideas, TODOs, and other notes.

If no one else is around and I feel like patiently dealing with speech recognition, I open Dragon Naturally Speaking and dictate the passages from the books. This helps me train Dragon’s speech model as well, which might be handy someday. If other people are around, though, I’ll just type in the segments from the book.

I usually type in my paper notes so that they’re more coherent (since I tend to write keywords instead of full thoughts). If I want to scan my index cards or notebook pages, I pop those into my Fujitsu Scansnap ix500 and scan them as JPG. I convert the JPGs to PNG and rename them with the date and title, and then I move them to a folder that gets automatically imported into Evernote. Evernote lets me search for text, and it also tries to find text even in scans of handwriting. It’s not perfect, but it’s decent, and I’ve learned to write more clearly because of that.

Looking things up: Since most of my notes are text, I can use grep to search through them.

Sharing what I know: I sometimes include excerpts or ideas in my blog posts. When I do, I link to the blog post from my text notes as well, so I can see what I’ve digested further and shared. If I’m reading a book that I know I’ll want to share with other people later (or if the authors have asked me nicely =) ), I sometimes visually summarize the book.

Following up on ideas: I add TODOs to my Org Mode agenda. I can also schedule reminders for things. I’m a little hesitant to add my books directory to the org-agenda-files list that Org Mode checks for TODO items (I have hundreds of book notes now!), so I’ve defined a custom agenda command that looks at just the book directory instead. Alternatively, it’s easy enough to grep the TODO keyword.

Planned improvements: I’m curious about the idea of a syntopicon, which I picked up from Adler and van Doren’s How to Read a Book. A syntopicon is a map of ideas across multiple books. With Org Mode’s support for indexing and links, I should be able to make something like it. I’m also looking forward to writing more about what I do with what I’m learning from books. This helps me challenge myself to learn actively instead of just letting a book flow through my brain.

Mmm, books!

If you’re curious, you can read about my past workflows:

Weekly review: Week ending November 14, 2014

I’m going to be out of commission for the next little while, probably resurfacing sometime late December. I’ve scheduled lots of posts in advance (I learned a lot over the past two weeks!), so you’ll still keep reading stuff, but I might be even slower to respond to things and I’ll probably stay close to home. Nothing to worry about, just something I have to take care of. Good thing there’s enough slack in my life for stuff like this.

Blog posts

Sketches

Focus areas and time review

  • Business (23.2h – 13%)
    • Earn (9.3h – 39% of Business)
      • E1: Plan how I can work from home for the next few weeks
      • Earn: E1: 1-2 days of consulting
    • Build (8.4h – 36% of Business)
      • Set up faster searching with ace-jump, isearch, helm-swoop
      • Drawing (1.4h)
      • Delegation (0.0h)
      • Packaging (0.0h)
      • Paperwork (1.5h)
        • Review bookkeeper feedback for tax return
    • Connect (5.5h – 23% of Business)
      • Coach Lucas on programming
  • Relationships (16.4h – 9%)
    • Follow up on insurance paperwork
  • Discretionary – Productive (15.9h – 9%)
    • Emacs (0.3h – 0% of all)
    • Quantified Awesome
      • Add want interface for groceries
      • Fix edit screen
      • I can restore an item
      • I can clear all crossed-off items
      • I can see when it was last synchronized
      • Our lists are synchronized
    • ER for I2
    • Cancel GICs
    • File insurance claims
    • Find a Japanese plain past tense practice tool
    • Find a Tableau e-book and put it on my laptop/tablet
    • Find an e-book for learning Japanese and put it on my laptop & tablet
    • Go through a Jasmine tutorial
    • Go through a QUnit tutorial
    • Minna no Nihongo Chapter 13-16
    • Move my dentist appointment
    • Set up folder synchronization for my tablet
    • Write about beginner steps for testing
    • Writing (7.5h)
  • Discretionary – Play (12.9h – 7%)
  • Personal routines (24.5h – 14%)
  • Unpaid work (4.5h – 2%)
  • Sleep (70.7h – 42% – average of 10.1 per day)

What could I do if I showed up in a bigger way?

I’m reading Ben Arment’s Dream Year: Make the Leap From a Job You Hate to a Life You Love (2014), and there’s a reminder in here about the choice between the fear of failure and the fear of insignificance. “Choose the fear of insignificance,” the author says. And I think: Hmm, actually, I’m okay with insignificance (or minor minor minor significance, in any case). Stoicism reminds us that after thousands of years, very little of this will matter. But maybe I should care a little bit. Since I’ve done all this work to minimize the fear of failure anyway. I might as well play on that side of the equation.

I’ve been thinking about this recently because I’m wondering whether I should take this experience in social business and make something bigger out of it. I could probably negotiate something with my main consulting clients so that we could get ideas or even code out in the wider world, or I could independently develop something that they and other people would be welcome to use. I haven’t quite sorted out what that would be like yet, but I imagine it would start off as open source components, then possibly consulting and product development once I’ve established a reputation in that community.

Of social business, Emacs, and blogging, though, I like Emacs the most. There’s something about it. I like the community a lot: interesting people doing interesting things, and a remarkably flexible platform that has kept me curious and fascinated for years. If I were to show up in a bigger way, I suppose that would involve writing more guides, and maybe understanding enough of the core of complex things like Org and Emacs itself so that I could contribute to the codebase. I tend to focus on workflow more than bugfixes or new features, though… I think there’s something interesting in how people use the same things in such different ways. Maybe I’ll write more about my evolving workflow, using that and personal projects as excuses to keep tweaking.

As for blogging, there are bucketloads of people who are happy to give other people advice on what to do and how to do it. I’m interested in keeping it unintimidating and useful for personal learning, but I’m more excited about and curious about those other two causes. Still, I can show by example, and I can offer advice and encouragement when people ask.

What are the differences between this slightly bigger life and my current one? I think part of it is related to the way that I’ve been minimizing my commitments during this 5-year experiment, being very careful about what I say yes to and what I promise my time towards. Part of it is taking the initiative instead of waiting for requests or sparks of inspiration. Part of it is working more deliberately towards a goal. It’s not going to be a big big life, but it might be interesting to experiment with.