{"id":"86944e959995fdeb","slug":"multi-line-chart","first_public_version":498,"paused_version":null,"likes":258,"publish_level":"live_unlisted","forks":0,"fork_of":null,"has_importers":true,"thumbnail":"efd2d99f7030626ac0e791b999c847b169ef7572c9956e0fb979ce01fb1ac687","default_thumbnail":"efd2d99f7030626ac0e791b999c847b169ef7572c9956e0fb979ce01fb1ac687","update_time":"2020-08-29T20:04:43.513Z","publish_time":"2023-07-04T15:43:10.379Z","publish_version":501,"latest_version":501,"roles":[],"sharing":null,"owner":{"id":"863951e3ebe4c0ae","avatar_url":"https://avatars.observableusercontent.com/avatar/5af16e327a90b2873351dda8a596c0d2d3bf954f64523deefe80177c9764d0f7","login":"d3","name":"D3","bio":"Bring your data to life.","home_url":"https://d3js.org","type":"team","tier":"pro_2024"},"creator":{"id":"074c414ad1d825f5","avatar_url":"https://avatars.observableusercontent.com/avatar/82811927da99f8938001b2ef1f552ad2c47083e46ebc55a3a146a5a5848c4519","login":"mbostock","name":"Mike Bostock","bio":"Visualization toolmaker. Founder @observablehq. Creator @d3. Former @nytgraphics. Pronounced BOSS-tock.","home_url":"https://bost.ocks.org/mike/","tier":"pro"},"authors":[{"id":"074c414ad1d825f5","avatar_url":"https://avatars.observableusercontent.com/avatar/82811927da99f8938001b2ef1f552ad2c47083e46ebc55a3a146a5a5848c4519","name":"Mike Bostock","login":"mbostock","bio":"Visualization toolmaker. Founder @observablehq. Creator @d3. Former @nytgraphics. Pronounced BOSS-tock.","home_url":"https://bost.ocks.org/mike/","tier":"pro","approved":true,"description":""}],"files":[{"id":"b154f8efd1a64d5e3e829b93e4fefd6d72219371ef440007fe368369fea0bbcdc9bcac06b72aae851f392411102931eba7774befa8c8fcbfcad4fc28f136ecd6","url":"https://static.observableusercontent.com/files/b154f8efd1a64d5e3e829b93e4fefd6d72219371ef440007fe368369fea0bbcdc9bcac06b72aae851f392411102931eba7774befa8c8fcbfcad4fc28f136ecd6","download_url":"https://static.observableusercontent.com/files/b154f8efd1a64d5e3e829b93e4fefd6d72219371ef440007fe368369fea0bbcdc9bcac06b72aae851f392411102931eba7774befa8c8fcbfcad4fc28f136ecd6?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%27unemployment.tsv","name":"unemployment.tsv","create_time":"2019-10-29T20:48:47.130Z","mime_type":"text/tab-separated-values","status":"public","size":32052,"content_encoding":"gzip","private_bucket_id":null},{"id":"a139d402230eaac422551e10fd6785ffa6fc986abe2648574a7361c1d93e15a686aee8ab7bfd6105bc1ee3fd3a33c6a1a6683963607ffe71171c325a6e476737","url":"https://static.observableusercontent.com/files/a139d402230eaac422551e10fd6785ffa6fc986abe2648574a7361c1d93e15a686aee8ab7bfd6105bc1ee3fd3a33c6a1a6683963607ffe71171c325a6e476737","download_url":"https://static.observableusercontent.com/files/a139d402230eaac422551e10fd6785ffa6fc986abe2648574a7361c1d93e15a686aee8ab7bfd6105bc1ee3fd3a33c6a1a6683963607ffe71171c325a6e476737?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%27bls-metro-unemployment.csv","name":"bls-metro-unemployment.csv","create_time":"2021-09-10T02:30:15.344Z","mime_type":"text/csv","status":"public","size":406211,"content_encoding":"gzip","private_bucket_id":null}],"comments":[{"id":"1878510b5731db5f","content":"I believe that under the pointerleft() function this part of the code path.style(\"mix-blend-mode\", \"multiply\").style(\"stroke\", null); should reference the mixBlendMode input variable rather than being \"multiply\" directly","node_id":29,"create_time":"2022-06-05T09:49:30.262Z","update_time":null,"resolved":true,"user":{"id":"e6a1a18b0d93cf75","avatar_url":"https://avatars.observableusercontent.com/avatar/1a0375306f317ee2df484583c00b91bd52104623222520ed669bc7749dd6a25f","login":"d-hash-code","name":"Darshan Hindocha","bio":"","home_url":"","tier":"public"}},{"id":"1d1fa9f617e46427","content":"Fixed, thank you!","node_id":29,"create_time":"2022-06-06T15:56:37.708Z","update_time":null,"resolved":true,"user":{"id":"45a379fcfcb14253","avatar_url":"https://avatars.observableusercontent.com/avatar/9cf371db2e1a2b4ed5f4a9783b6954cf7aeb2a88c56d4054c96ef360cf85ef89","login":"fil","name":"Fil","bio":"Vocateur.","home_url":"https://visionscarto.net/","tier":"pro"}}],"commenting_lock":null,"suggestions_to":[],"suggestion_from":null,"collections":[{"id":"2a5376aca044b34e","type":"public","slug":"charts","title":"D3 Charts","description":"Older D3 examples, deprecated.","update_time":"2023-07-04T09:37:08.122Z","pinned":false,"ordered":true,"custom_thumbnail":null,"default_thumbnail":"2471c1145e33b39a1d5e38b7bb5460a2620aadf1c32717a270347620b1e981bd","thumbnail":"2471c1145e33b39a1d5e38b7bb5460a2620aadf1c32717a270347620b1e981bd","listing_count":11,"parent_collection_count":0,"owner":{"id":"863951e3ebe4c0ae","avatar_url":"https://avatars.observableusercontent.com/avatar/5af16e327a90b2873351dda8a596c0d2d3bf954f64523deefe80177c9764d0f7","login":"d3","name":"D3","bio":"Bring your data to life.","home_url":"https://d3js.org","type":"team","tier":"pro_2024"}}],"version":299,"title":"Multi-Line Chart","license":"isc","copyright":"Copyright 2018–2020 Observable, Inc.","nodes":[{"id":1,"value":"md`# Multi-Line Chart\n\nData: [Bureau of Labor Statistics](https://www.bls.gov/)`","pinned":false,"mode":"js","data":null,"name":null},{"id":29,"value":"chart = {\n  const svg = d3.create(\"svg\")\n      .attr(\"viewBox\", [0, 0, width, height])\n      .style(\"overflow\", \"visible\");\n\n  svg.append(\"g\")\n      .call(xAxis);\n\n  svg.append(\"g\")\n      .call(yAxis);\n\n  const path = svg.append(\"g\")\n      .attr(\"fill\", \"none\")\n      .attr(\"stroke\", \"steelblue\")\n      .attr(\"stroke-width\", 1.5)\n      .attr(\"stroke-linejoin\", \"round\")\n      .attr(\"stroke-linecap\", \"round\")\n    .selectAll(\"path\")\n    .data(data.series)\n    .join(\"path\")\n      .style(\"mix-blend-mode\", \"multiply\")\n      .attr(\"d\", d => line(d.values));\n\n  svg.call(hover, path);\n\n  return svg.node();\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":3,"value":"data = {\n  const data = d3.tsvParse(await FileAttachment(\"unemployment.tsv\").text());\n  const columns = data.columns.slice(1);\n  return {\n    y: \"% Unemployment\",\n    series: data.map(d => ({\n      name: d.name.replace(/, ([\\w-]+).*/, \" $1\"),\n      values: columns.map(k => +d[k])\n    })),\n    dates: columns.map(d3.utcParse(\"%Y-%m\"))\n  };\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":230,"value":"function hover(svg, path) {\n  \n  if (\"ontouchstart\" in document) svg\n      .style(\"-webkit-tap-highlight-color\", \"transparent\")\n      .on(\"touchmove\", moved)\n      .on(\"touchstart\", entered)\n      .on(\"touchend\", left)\n  else svg\n      .on(\"mousemove\", moved)\n      .on(\"mouseenter\", entered)\n      .on(\"mouseleave\", left);\n\n  const dot = svg.append(\"g\")\n      .attr(\"display\", \"none\");\n\n  dot.append(\"circle\")\n      .attr(\"r\", 2.5);\n\n  dot.append(\"text\")\n      .attr(\"font-family\", \"sans-serif\")\n      .attr(\"font-size\", 10)\n      .attr(\"text-anchor\", \"middle\")\n      .attr(\"y\", -8);\n\n  function moved(event) {\n    event.preventDefault();\n    const pointer = d3.pointer(event, this);\n    const xm = x.invert(pointer[0]);\n    const ym = y.invert(pointer[1]);\n    const i = d3.bisectCenter(data.dates, xm);\n    const s = d3.least(data.series, d => Math.abs(d.values[i] - ym));\n    path.attr(\"stroke\", d => d === s ? null : \"#ddd\").filter(d => d === s).raise();\n    dot.attr(\"transform\", `translate(${x(data.dates[i])},${y(s.values[i])})`);\n    dot.select(\"text\").text(s.name);\n  }\n\n  function entered() {\n    path.style(\"mix-blend-mode\", null).attr(\"stroke\", \"#ddd\");\n    dot.attr(\"display\", null);\n  }\n\n  function left() {\n    path.style(\"mix-blend-mode\", \"multiply\").attr(\"stroke\", null);\n    dot.attr(\"display\", \"none\");\n  }\n}","pinned":true,"mode":"js","data":null,"name":null},{"id":44,"value":"height = 600","pinned":true,"mode":"js","data":null,"name":null},{"id":48,"value":"margin = ({top: 20, right: 20, bottom: 30, left: 30})","pinned":true,"mode":"js","data":null,"name":null},{"id":52,"value":"x = d3.scaleUtc()\n    .domain(d3.extent(data.dates))\n    .range([margin.left, width - margin.right])","pinned":true,"mode":"js","data":null,"name":null},{"id":61,"value":"y = d3.scaleLinear()\n    .domain([0, d3.max(data.series, d => d3.max(d.values))]).nice()\n    .range([height - margin.bottom, margin.top])","pinned":true,"mode":"js","data":null,"name":null},{"id":70,"value":"xAxis = g => g\n    .attr(\"transform\", `translate(0,${height - margin.bottom})`)\n    .call(d3.axisBottom(x).ticks(width / 80).tickSizeOuter(0))","pinned":true,"mode":"js","data":null,"name":null},{"id":74,"value":"yAxis = g => g\n    .attr(\"transform\", `translate(${margin.left},0)`)\n    .call(d3.axisLeft(y))\n    .call(g => g.select(\".domain\").remove())\n    .call(g => g.select(\".tick:last-of-type text\").clone()\n        .attr(\"x\", 3)\n        .attr(\"text-anchor\", \"start\")\n        .attr(\"font-weight\", \"bold\")\n        .text(data.y))","pinned":true,"mode":"js","data":null,"name":null},{"id":65,"value":"line = d3.line()\n    .defined(d => !isNaN(d))\n    .x((d, i) => x(data.dates[i]))\n    .y(d => y(d))","pinned":true,"mode":"js","data":null,"name":null},{"id":6,"value":"d3 = require(\"d3@^6.1\")","pinned":true,"mode":"js","data":null,"name":null}],"resolutions":[],"schedule":null,"last_view_time":null}