├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── d3-selection-multi.sublime-project ├── index.js ├── package.json ├── src ├── selection │ ├── attrs.js │ ├── properties.js │ └── styles.js └── transition │ ├── attrs.js │ └── styles.js └── test └── selection └── attrs-test.js /.eslintrc: -------------------------------------------------------------------------------- 1 | parserOptions: 2 | sourceType: "module" 3 | 4 | env: 5 | es6: true 6 | browser: true 7 | 8 | extends: 9 | "eslint:recommended" 10 | 11 | rules: 12 | no-cond-assign: 0 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-workspace 2 | .DS_Store 3 | build/ 4 | node_modules 5 | npm-debug.log 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.sublime-* 2 | build/*.zip 3 | test/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2016, Michael Bostock 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * The name Michael Bostock may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # d3-selection-multi 2 | 3 | This module adds multi-value syntax to [selections](https://github.com/d3/d3-selection) and [transitions](https://github.com/d3/d3-transition), allowing you to set multiple attributes, styles or properties simultaneously with more concise syntax. For example: 4 | 5 | ```js 6 | d3.select("body").append("div") 7 | .attrs({ 8 | title: "A cheery, timeless greeting.", 9 | class: "greeting" 10 | }) 11 | .text("Hello, world!"); 12 | ``` 13 | 14 | This is equivalent to: 15 | 16 | ```js 17 | d3.select("body").append("div") 18 | .attr("title", "A cheery, timeless greeting.") 19 | .attr("class", "greeting") 20 | .text("Hello, world!"); 21 | ``` 22 | 23 | Like [*selection*.attr](https://github.com/d3/d3-selection#selection_attr), the values in the multi-value object can be functions of data: 24 | 25 | ```js 26 | d3.select("body").append("div") 27 | .attrs({ 28 | title: function(d) { return d.title; }, 29 | id: function(d, i) { return "id-" + i; }, 30 | }); 31 | ``` 32 | 33 | Alternatively, you can pass a function which returns an object, allowing you to share some computational effort across multiple attributes, or to determine which attribute to set dynamically: 34 | 35 | ```js 36 | d3.select("body").append("div") 37 | .attrs(function(d, i) { return {title: d.title, id: "id-" + i}; }); 38 | ``` 39 | 40 | This module is not included in the [default D3 bundle](https://github.com/d3/d3) for parsimony’s sake: the single-value methods such as [*selection*.attr](https://github.com/d3/d3-selection#selection_attr) are recommended for most users, as there is little benefit to the shorter syntax provided by these convenience method. 41 | 42 | ## Installing 43 | 44 | If you use NPM, `npm install d3-selection-multi`. Otherwise, download the [latest release](https://github.com/d3/d3-selection-multi/releases/latest) or load directly from [d3js.org](https://d3js.org) as a [standalone library](https://d3js.org/d3-selection-multi.v1.min.js). AMD, CommonJS, and vanilla environments are supported. In vanilla, a `d3` global is exported: 45 | 46 | ```html 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 62 | ``` 63 | 64 | Or, in combination with the [D3 default bundle](https://github.com/d3/d3): 65 | 66 | ```html 67 | 68 | 69 | 76 | ``` 77 | 78 | [Try d3-selection-multi in your browser.](https://tonicdev.com/npm/d3-selection-multi) 79 | 80 | ## API Reference 81 | 82 | # selection.attrs(values) 83 | 84 | A convenience method on top of [*selection*.attr](https://github.com/d3/d3-selection#selection_attr) for setting multiple attributes. If the specified *values* is an object, the values may be specified either as strings or functions. For example: 85 | 86 | ```js 87 | selection.attrs({foo: "foo-value", bar: function(d) { return d.bar; }}); 88 | ``` 89 | 90 | If a value is a constant, all elements are given the same attribute value; otherwise, if a value is a function, the function is evaluated for each selected element, in order, being passed the current datum (*d*), the current index (*i*), and the current group (*nodes*), with *this* as the current DOM element. The function’s return value is then used to set each element’s attribute. A null value will remove the specified attribute. 91 | 92 | If the specified *values* is a function, the function is evaluated for each selected element, in order, being passed the current datum (*d*), the current index (*i*), and the current group (*nodes*), with *this* as the current DOM element. The function’s return value must be an object with string values, which are then used to set attributes on the current element. For example: 93 | 94 | ```js 95 | selection.attrs(function(d) { return {foo: "foo-value", bar: d.bar}; }); 96 | ``` 97 | 98 | Passing a function to *selection*.attrs is convenient for sharing some computational effort or state across multiple attributes, or for specifying dynamically which attributes to set. 99 | 100 | # selection.styles(values[, priority]) 101 | 102 | A convenience method on top of [*selection*.style](https://github.com/d3/d3-selection#selection_style) for setting multiple style properties. If the specified *values* is an object, the values may be specified either as strings or functions. For example: 103 | 104 | ```js 105 | selection.styles({fill: "red", stroke: function(d) { return d.stroke; }}); 106 | ``` 107 | 108 | If a value is a constant, all elements are given the same style property value; otherwise, if a value is a function, the function is evaluated for each selected element, in order, being passed the current datum (*d*), the current index (*i*), and the current group (*nodes*), with *this* as the current DOM element. The function’s return value is then used to set each element’s style properties. A null value will remove the specified style properties. 109 | 110 | If the specified *values* is a function, the function is evaluated for each selected element, in order, being passed the current datum (*d*), the current index (*i*), and the current group (*nodes*), with *this* as the current DOM element. The function’s return value must be an object with string values, which are then used to set style properties on the current element. For example: 111 | 112 | ```js 113 | selection.styles(function(d) { return {fill: "red", stroke: d.stroke}; }); 114 | ``` 115 | 116 | Passing a function to *selection*.styles is convenient for sharing some computational effort or state across multiple style properties, or for specifying dynamically which style properties to set. 117 | 118 | # selection.properties(values) 119 | 120 | A convenience method on top of [*selection*.property](https://github.com/d3/d3-selection#selection_property) for setting multiple element properties. If the specified *values* is an object, the values may be specified either as strings or functions. For example: 121 | 122 | ```js 123 | selection.properties({foo: "foo-value", id: function(d, i) { return "id-" + i; }}); 124 | ``` 125 | 126 | If a value is a constant, all elements are given the same property value; otherwise, if a value is a function, the function is evaluated for each selected element, in order, being passed the current datum (*d*), the current index (*i*), and the current group (*nodes*), with *this* as the current DOM element. The function’s return value is then used to set each element’s properties. A null value will remove the specified properties. 127 | 128 | If the specified *values* is a function, the function is evaluated for each selected element, in order, being passed the current datum (*d*), the current index (*i*), and the current group (*nodes*), with *this* as the current DOM element. The function’s return value must be an object with string values, which are then used to set properties on the current element. For example: 129 | 130 | ```js 131 | selection.properties(function(d, i) { return {foo: "foo-value", id: "id-" + i}; }); 132 | ``` 133 | 134 | Passing a function to *selection*.properties is convenient for sharing some computational effort or state across multiple properties, or for specifying dynamically which properties to set. 135 | 136 | # transition.attrs(values) 137 | 138 | Like [*selection*.attrs](#selection_attrs), but for [*transition*.attr](https://github.com/d3/d3-transition#transition_attr). 139 | 140 | # transition.styles(values[, priority]) 141 | 142 | Like [*selection*.styles](#selection_styles), but for [*transition*.style](https://github.com/d3/d3-transition#transition_style). 143 | -------------------------------------------------------------------------------- /d3-selection-multi.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": ".", 5 | "file_exclude_patterns": [ 6 | "*.sublime-workspace" 7 | ], 8 | "folder_exclude_patterns": [ 9 | "build" 10 | ] 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import {selection} from "d3-selection"; 2 | import {transition} from "d3-transition"; 3 | import selection_attrs from "./src/selection/attrs"; 4 | import selection_styles from "./src/selection/styles"; 5 | import selection_properties from "./src/selection/properties"; 6 | import transition_attrs from "./src/transition/attrs"; 7 | import transition_styles from "./src/transition/styles"; 8 | 9 | selection.prototype.attrs = selection_attrs; 10 | selection.prototype.styles = selection_styles; 11 | selection.prototype.properties = selection_properties; 12 | transition.prototype.attrs = transition_attrs; 13 | transition.prototype.styles = transition_styles; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d3-selection-multi", 3 | "version": "1.0.1", 4 | "description": "Multi-value map syntax for D3 selections.", 5 | "keywords": [ 6 | "d3", 7 | "d3-module", 8 | "dom", 9 | "selection" 10 | ], 11 | "homepage": "https://github.com/d3/d3-selection-multi", 12 | "license": "BSD-3-Clause", 13 | "author": { 14 | "name": "Mike Bostock", 15 | "url": "http://bost.ocks.org/mike" 16 | }, 17 | "main": "build/d3-selection-multi.js", 18 | "jsnext:main": "index", 19 | "module": "index", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/d3/d3-selection-multi.git" 23 | }, 24 | "scripts": { 25 | "pretest": "rm -rf build && mkdir build && rollup --banner \"$(preamble)\" -f umd -g d3-selection:d3,d3-transition:d3 -n d3 -o build/d3-selection-multi.js -- index.js", 26 | "test": "tape 'test/**/*-test.js' && eslint index.js src", 27 | "prepublish": "npm run test && uglifyjs --preamble \"$(preamble)\" build/d3-selection-multi.js -c -m -o build/d3-selection-multi.min.js", 28 | "postpublish": "git push && git push --tags && cd ../d3.github.com && git pull && cp ../d3-selection-multi/build/d3-selection-multi.js d3-selection-multi.v1.js && cp ../d3-selection-multi/build/d3-selection-multi.min.js d3-selection-multi.v1.min.js && git add d3-selection-multi.v1.js d3-selection-multi.v1.min.js && git commit -m \"d3-selection-multi ${npm_package_version}\" && git push && cd - && zip -j build/d3-selection-multi.zip -- LICENSE README.md build/d3-selection-multi.js build/d3-selection-multi.min.js" 29 | }, 30 | "dependencies": { 31 | "d3-selection": "1", 32 | "d3-transition": "1" 33 | }, 34 | "devDependencies": { 35 | "eslint": "3", 36 | "jsdom": "9", 37 | "package-preamble": "0.0", 38 | "rollup": "0.41", 39 | "tape": "4", 40 | "uglify-js": "2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/selection/attrs.js: -------------------------------------------------------------------------------- 1 | import {select} from "d3-selection"; 2 | 3 | function attrsFunction(selection, map) { 4 | return selection.each(function() { 5 | var x = map.apply(this, arguments), s = select(this); 6 | for (var name in x) s.attr(name, x[name]); 7 | }); 8 | } 9 | 10 | function attrsObject(selection, map) { 11 | for (var name in map) selection.attr(name, map[name]); 12 | return selection; 13 | } 14 | 15 | export default function(map) { 16 | return (typeof map === "function" ? attrsFunction : attrsObject)(this, map); 17 | } 18 | -------------------------------------------------------------------------------- /src/selection/properties.js: -------------------------------------------------------------------------------- 1 | import {select} from "d3-selection"; 2 | 3 | function propertiesFunction(selection, map) { 4 | return selection.each(function() { 5 | var x = map.apply(this, arguments), s = select(this); 6 | for (var name in x) s.property(name, x[name]); 7 | }); 8 | } 9 | 10 | function propertiesObject(selection, map) { 11 | for (var name in map) selection.property(name, map[name]); 12 | return selection; 13 | } 14 | 15 | export default function(map) { 16 | return (typeof map === "function" ? propertiesFunction : propertiesObject)(this, map); 17 | } 18 | -------------------------------------------------------------------------------- /src/selection/styles.js: -------------------------------------------------------------------------------- 1 | import {select} from "d3-selection"; 2 | 3 | function stylesFunction(selection, map, priority) { 4 | return selection.each(function() { 5 | var x = map.apply(this, arguments), s = select(this); 6 | for (var name in x) s.style(name, x[name], priority); 7 | }); 8 | } 9 | 10 | function stylesObject(selection, map, priority) { 11 | for (var name in map) selection.style(name, map[name], priority); 12 | return selection; 13 | } 14 | 15 | export default function(map, priority) { 16 | return (typeof map === "function" ? stylesFunction : stylesObject)(this, map, priority == null ? "" : priority); 17 | } 18 | -------------------------------------------------------------------------------- /src/transition/attrs.js: -------------------------------------------------------------------------------- 1 | import {select} from "d3-selection"; 2 | 3 | function attrsFunction(transition, map) { 4 | return transition.each(function() { 5 | var x = map.apply(this, arguments), t = select(this).transition(transition); 6 | for (var name in x) t.attr(name, x[name]); 7 | }); 8 | } 9 | 10 | function attrsObject(transition, map) { 11 | for (var name in map) transition.attr(name, map[name]); 12 | return transition; 13 | } 14 | 15 | export default function(map) { 16 | return (typeof map === "function" ? attrsFunction : attrsObject)(this, map); 17 | } 18 | -------------------------------------------------------------------------------- /src/transition/styles.js: -------------------------------------------------------------------------------- 1 | import {select} from "d3-selection"; 2 | 3 | function stylesFunction(transition, map, priority) { 4 | return transition.each(function() { 5 | var x = map.apply(this, arguments), t = select(this).transition(transition); 6 | for (var name in x) t.style(name, x[name], priority); 7 | }); 8 | } 9 | 10 | function stylesObject(transition, map, priority) { 11 | for (var name in map) transition.style(name, map[name], priority); 12 | return transition; 13 | } 14 | 15 | export default function(map, priority) { 16 | return (typeof map === "function" ? stylesFunction : stylesObject)(this, map, priority == null ? "" : priority); 17 | } 18 | -------------------------------------------------------------------------------- /test/selection/attrs-test.js: -------------------------------------------------------------------------------- 1 | var tape = require("tape"), 2 | jsdom = require("jsdom"), 3 | d3 = require("d3-selection"); 4 | 5 | require("../../"); 6 | 7 | tape("selection.attrs sets attributes on selected elements", function(test) { 8 | var document = jsdom.jsdom(); 9 | d3.select(document.body).attrs({foo: 42, bar: "BAR"}); 10 | test.equal(document.body.getAttribute("foo"), "42"); 11 | test.equal(document.body.getAttribute("bar"), "BAR"); 12 | d3.select(document.body).attrs({foo: null}); 13 | test.equal(document.body.hasAttribute("foo"), false); 14 | test.end(); 15 | }); 16 | 17 | tape("selection.attrs can take a function that returns an object", function(test) { 18 | var document = jsdom.jsdom("