Week 1a - Introduction to HTML and Javascript
Please fill out this survey! https://bit.ly/2RW13Qq
HTML: The Core Concepts HTML stands for HyperText Markup Language, which creates a structure to hold the content of your webpage; it does this using what it calls elements. All HTML documents start with the following line of code:
html`<!DOCTYPE html>`
This tag is a way of giving your browser an explicit heads up that it should expect to be interpreting and displaying HTML. HTML is a markup framework language and is the lingua franca of the internet. You can think of a markup language as a way of imposing structure onto content, which allow you to style and interact with your design in a predicable way. While, in recent years, it's become more and more common to develop websites in Python (using a framework like Django) or Ruby (using a framework like Rails), these and other frameworks ultimately send documents to your web browser as HTML.
Content in HTML is always organized into elements, which can then be filled with content, made interactive, and styled.
html`<element>Content goes here!</element>`
Some elements are contained in one tag, such as images. These elements are called void elements.
html`<element />`
Comments look like the following. They are used to write human-readable notes in your code, but are ignored by the browser.
html`<!-- This is a comment -->`
A basic page, all together, will generally look something like this.
html` \`<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Hello World HTML Document</title> </head> <body> <!-- This is a comment --> <h1>Hello World</h1> <div id="main"> ... </div> </body> </html> \` `
The Document Object Model (DOM) Elements in a HTML document are organized as a Document Object Model (or DOM). The Document Object Model represents the hierarchy of elements in our page. More than that, though: it is also serves as an interface that allows programs and scripts to dynamically access and update the content, style, and structure of the page. You can diagram the HTML DOM Tree, and it consists of our HTML elements.
High-level structure The <html></html>, <head></head>, and <body></body> elements are large containers that are present in every HTML file. They provide the highest-level structure for our document, separating metadata, stylesheets, and linked scripts from the body of our page.
html` <html lang="en"> <!-- HEAD element containing meta information, style, and links --> <head>This is my first HTML document (This Document can be written without the hierachy, but it is hard to read). </head> <!-- BODY element containing all document content elements --> <body> ... </body> </html> `
Links and Images Links and images are referenced using the <a></a> and <img/> elements, respectively. For links, the link url goes in the href attribute and the link text goes in between the tags.
html`<a href="https://docs.google.com/spreadsheets/d/1ZmIsti5yX9vpd01ZNN1Pmw5Y6bYIXNKRz7l7zjVxFGQ/edit#gid=0">This is my working schedule</a>`
An image tag is used to link an image to your document. The location of the image is specified using the src attribute. Note that the <img/> tag is a void element - where most HTML tags require an opening and a closing tag, a single <img/> tag is sufficient to delineate a complete image element.
html`<img src="http://duspviz.mit.edu/_assets/img/tutorials.png"> />`
Paragraphs and Spans We can create paragraphs, conventionally containing longer blocks of body text, using the <p></p> element.
html` <p>This is one paragraph.</p><p>This is a second.</p> `
Lists Lists are useful for itemized information. List tags can be specified inline with one another, but they are more human-readable when they are on separate lines.
html` <!-- UL defines an unordered list --> <ul> <li>Ham.</li><li>Cheese.</li><li>Bread.</li> </ul> <!-- OL defines an ordered list --> <ol> <li>Cheesecake.</li> <li>Family.</li> <li>Friends.</li> </ol> <!-- A Description list, useful for pairing terms with definitions--> <dl> <!-- term --> <dt>Coffee</dt> <!-- description or definition --> <dd>Hot delicious drink.</dd> </dl> `
DIV Tags and Spans One of the most common tags (and one of the more difficult to grasp, initially) is the <div></div> tag. This tag is used to separate out a division or section of an HTML page. One page can contain many such elements, and they can nest endlessly. We can also create groups of inline elements (multiple elements that should appear on the same line) using the <span></span> tag. Neither divs nor spans have any inherent form, meaning that they are used to group content into containers (or divisions) that are styled and placed using CSS (Cascading Style Sheets). CSS is a styling language used for describing the style of an HTML page. We will introduce it in the next step.
html` <p>Span allows you to classify and style similar elements throughout a page, like <span class="example">location names</span>, <span class="example">ingredients</span>, or <span class="example">key terms</span>, inline.</p> <div id="section2"> Div allows you to classify and separate blocks or sections of webpages. </div> `
Attributes, Classes, and IDs Tags are specified and defined using attributes, classes, and IDs. These attributes, classes, and IDs allow you to identify specific elements, modify individual elements and groups of elements, and set the characteristics of the elements. Attributes specify properties of elements. Elements can have multiple attributes. For example, a link (<a></a>, remember?) will can include an href = "" that specifies the link destination alongside the attribute target = "_blank" which instructs the web browser to open the link in a new tab. Class is a special attribute that identifies a group of elements that operate similarly or work in the same fashion. These are specified using the class = "classname". The advantage of this is that you can efficiently specify the same style or behavior for many elements. ID is an attribute that identifies unique features. In other words: if you specify id = "specialthing", there should be no other elements with the id specialthing. Each ID should be unique. This allows you to specify operations and styles that are unique to that feature.
Styling HTML Elements Styling of HTML elements is usually done through Cascading Style Sheets (CSS). CSS is a styling language used for describing the look and formatting of the elements in an HTML page. It uses the DOM as the way it interfaces with the elemebts, and styles 'cascade' from higher elements in the DOM tree to elements further down. However, in this first section of the course we will be styling the HTML elements in-line instead!
Styling By Element If we want to change the style of all of a certain kind of element (like changing the color of links), we can do so by declaring style changes using the tag.
html`<h4 style="text-align: center;">Centered Header</h4>`
html`<h2 id="header" style="background:red">This is a big header</h2>`
Writing HTML and Markdown in Observable In the introduction to Observable notebook, we looked at cells that return data structures such as numbers, strings, arrays and objects. But what if we want an interactive visualization? Or for that matter, just some text? If a cell returns a DOM element, that DOM element is displayed as-is!
html`<p>I am a <i>paragraph</i> element!</p>`
A cell referencing another cell in HTML is re-evaluated automatically when the referenced value changes. Cells can generate DOM (HTML, SVG, Canvas, WebGL, etc.). You can use the standard DOM API like document.createElement, or use the built-in html tagged template literal:
html`<span style="background:yellow;"> My favorite language is <i>HTML</i>. </span>`
color = "red"
DOM can be made reactive simply by referring to other cells. The next cell refers to color. (Try editing the definition of color above.)
html`My favorite color is <i style="background:${color};">${color}</i>.`
Writing Markdown Even better, you can write Markdown:
I, too, am a paragraph element.
This is a header
Markdown supports a variety of styles and structural elements, such as lists and tables. See GitHub’s guide to Markdown for more. Since all cells in Observable are JavaScript—a “Markdown cell” is just a JavaScript cell of a Markdown tagged template literal—Markdown and HTML cells can be dynamic: you can embed expressions ${…} in template literals. For example, the favorite number below is randomly generated.
My favorite number is .
Embedded expressions can also be DOM nodes. This lets you embed dynamic content, such as little charts in SVG or mathematical notation in LaTeX, within Markdown.
I like Markdown for prose, but for math.
A sparkline is a chart inline with prose.
function sparkline(values, width = 64, height = 17) { const x = d3.scaleLinear().domain([0, values.length - 1]).range([0.5, width - 0.5]); const y = d3.scaleLinear().domain(d3.extent(values)).range([height - 0.5, 0.5]); const context = DOM.context2d(width, height); const line = d3.line().x((d, i) => x(i)).y(y).context(context); context.beginPath(), line(values), context.stroke(); return context.canvas; }
And of course, Markdown can be reactive, too! The now built-in from the Observable standard library is a reactive variable whose value is the current time. So, embedding a reference to now causes the Markdown to re-evaluate continuously:
The current time is .
You can manipulate DOM generated from Markdown with JavaScript, say to mix dynamic and static content. The text below cycles through D3’s rainbow scheme.
{ const p = md`Less-angry rainbows are the *best* rainbows.`; p.style.color = d3.interpolateRainbow(now / 2000); return p; }
Another common type of dynamic content is Canvas. The standard library method DOM.context2d returns a CanvasRenderingContext2D of the specified width and height (and automatic scaling for high-resolution displays). Draw to the context, and then return the canvas to display it in the notebook.
{ const context = DOM.context2d(128, 128); for (let radius = 8; radius < 128; radius += 8) { context.beginPath(); context.arc(0, 0, radius, 0, 2 * Math.PI); context.stroke(); } return context.canvas; }
Or, as SVG:
html`<svg width="128" height="128" fill="none" stroke="black">${ d3.range(8, 128, 8).map(radius => `<circle r="${radius}"></circle>`) }</svg>`
Often you’ll use a helper library such as D3 to generate dynamic content. We’ll cover third-party libraries in more detail later in the introduction, but here’s a quick example generating colorful concentric circles in SVG.
d3 = require("d3@5")
{ // Create a square <svg> 128px by 128px. const svg = DOM.svg(128, 128); // Compute various radii 128, 120, 112, 104 etc. const radii = d3.range(128, 0, -8); // Create a sequential color scale (dark for small radii, bright for large). const color = d3.scaleSequential(d3.interpolateViridis).domain([0, 128]); // Use D3’s data join to create circles. d3.select(svg) .selectAll("circle") .data(radii) .enter().append("circle") .attr("fill", radius => color(radius)) .attr("r", radius => radius); return svg; }
Or, more succinctly as a single expression:
d3.select(DOM.svg(128, 128)) .call(svg => svg.selectAll("circle") .data(d3.range(128, 0, -8)) .enter().append("circle") .attr("fill", d3.scaleSequential(d3.interpolateViridis).domain([0, 128])) .attr("r", d => d)) .node()
For the mathematically inclined, you can write , either inline or as blocks:
tex`E = mc^2`
tex `E = mc^2`
As with any cell, you can create a cell that returns a DOM element as a variable name:
slider = html`<input type=range>`
slider2 = html `<input type = range>`
And you can read properties of a DOM element defined in another cell:
slider.type
slider.value
But note that properties of a DOM element are not automatically reactive: a cell that references slider.value won’t be re-evaluated automatically when the slider changes. DOM elements are mutable, so be careful when referencing an element defined in another cell, and you should usually avoid mutating elements defined in other cells. To react to an input element’s changing value, use viewof:
viewof value = html`<input type=range>`
value
A cell can return a DOM element that’s already being displayed somewhere else on the page. In this case, the value inspector is used, rather than showing the element inline.
slider // This slider is already visible above!
Helpful Styling Tips Defining Color We've seen above how to change font and background color. Colors can specified using hexadecimal values, RGB values, or a set of preset supported color names. I find it helpful to use a utility like Adobe Color or Color Hex to locate a color I like. These are three ways of making my font red:
`#foo { color: #ff0000; /* color: rgb(255, 0, 0); */ /* color: red; */ }`
Introduction to require require lets you use thousands of open source JavaScript modules in your notebooks. If you can think of a task, like date manipulation, math, or music, there’s probably already a module that can help. Just like you can reuse code from other notebooks using imports, you can require code from JavaScript modules using require. Here’s an example of using require to include Simple Statistics, a module that includes many statistical functions, and then using one of those functions to compute a standard deviation.
simpleStatistics = require('simple-statistics')
simpleStatistics.standardDeviation([1, 2, 3])
require's input The argument to the require function can be any of: A module name The most common case, where you simply want to include a module. For example, to require the moment date & time library:
moment = require('moment')
moment('January 2, 2018').subtract(1, 'year').year()
That module name can also include a version. You can include a semantic version specifier to allow for a range of versions, like the require statement below, which includes the newest release that matches 2.x.x.
moment2 = require('moment@2')
A module name and a path Sometimes you want to include an individual file from a module, rather than its main exported interface. This is much less common, but can be useful for modules that have addons or that don’t correctly tell unpkg which file to expose as the default. For example, here's how we can include the Chart.js module, which includes an AMD file that require can use, but doesn’t point to it by default: instead of relying on auto-detection, we specify the file directly:
Chart = require('chart.js/dist/Chart.min.js')
A URL You can also specify a full URL to require - this lets you point require at any server, not just unpkg. For example, we can load the latest version of ganja.js directly from GitHub (via RawGit).*
ganja = require('https://cdn.rawgit.com/enkimute/ganja.js/master/ganja.js')
Multiple inputs Specifying multiple inputs - multiple module names, or URLs, or module names with a path, will load all of the named things and combine their exports into one object. This is especially useful for modules like D3’s constituent modules.
// Only load the DSV and Selection parts of D3, and combine their exports into one object d3CsvAndFetch = require("d3-dsv", "d3-selection")
In some cases, too, require won’t find a module or won’t be able to load it - and it’ll return a promise rejection.
notValid = require('not-a-real-npm-module')
If you’re running into issues requiring a module, try using the module require debugger, or asking in the forum.
require’s behavior By default, the require function uses modules published on NPM, a service that hosts over 600,000 different modules* created by thousands of individual developers. Because notebooks run in a web environment, we use another service, unpkg.com, that takes NPM's modules and makes them accessible to browsers. For example, if we call require with the argument d3, it expands that argument into http://unpkg.com/d3, which then redirects to the source code of the D3 visualization library:
d3test = require('d3')
import(): a way to require ES6 modules In addition to require, which uses the widely-supported AMD standard, we also support import(), a new way of requiring modules that aims to eventually obviate the need for custom functions like require. import() is a native browser feature that’s just now gaining adoption — it only works in the newest versions of Chrome, Safari, Firefox, and Edge. It also requires modules to be published in the ES6 module specification, which is still gaining adoption. Like require, there is a shortcut so that you can refer to modules by name with import() and it’ll load them from unpkg.com, if they’re available - or you can specify a full URL and it’ll pull from any other source.
scale = import('d3-scale')