├── .gitignore
├── docs
├── images
│ └── vanilla-js-logo.png
├── styles
│ ├── vanilla-js-tabs.css
│ ├── docs-page.css
│ ├── prism.css
│ └── docs-page.less
├── javascript
│ ├── vanilla-js-tabs.min.js
│ └── prism.js
└── index.html
├── tsconfig.json
├── dist
├── vanilla-js-tabs.css
├── vanilla-js-tabs.min.js
├── index.html
└── vanilla-js-tabs.js
├── src
├── styles
│ └── vanilla-js-tabs.less
├── index.pug
└── javascript
│ └── vanilla-js-tabs.ts
├── .jshintrc
├── LICENSE
├── package.json
├── test
└── spec
│ ├── fixtures
│ └── tabs.fixture.html
│ └── tabs.spec.ts
├── gulpfile.js
├── karma.conf.js
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /coverage
--------------------------------------------------------------------------------
/docs/images/vanilla-js-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zoltantothcom/vanilla-js-tabs/HEAD/docs/images/vanilla-js-logo.png
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es5",
5 | "lib": ["es5", "dom"],
6 | "sourceMap": true,
7 | "declaration": false,
8 | "noImplicitAny": true,
9 | "strictNullChecks": true,
10 | "types": ["jasmine", "jasmine-jquery"]
11 | },
12 | "include": ["src/**/*.ts", "test/**/*.ts"],
13 | "exclude": ["node_modules"]
14 | }
15 |
--------------------------------------------------------------------------------
/dist/vanilla-js-tabs.css:
--------------------------------------------------------------------------------
1 | .js-tabs {
2 | margin: 2em;
3 | max-width: 100%;
4 | }
5 | .js-tabs__header {
6 | display: block;
7 | margin: 0;
8 | padding: 0;
9 | overflow: hidden;
10 | }
11 | .js-tabs__header li {
12 | display: inline-block;
13 | float: left;
14 | }
15 | .js-tabs__title {
16 | background: #f5f5f5;
17 | border: 1px solid #ccc;
18 | cursor: pointer;
19 | display: block;
20 | margin-right: 0.5em;
21 | padding: 1em 1.5em;
22 | transition: all 0.25s;
23 | }
24 | .js-tabs__title:hover {
25 | text-decoration: none;
26 | }
27 | .js-tabs__title-active {
28 | background: #fff;
29 | border-bottom-color: #fff;
30 | border-top-left-radius: 0.75em;
31 | }
32 | .js-tabs__content {
33 | border: 1px solid #ccc;
34 | line-height: 1.5;
35 | margin-top: -1px;
36 | padding: 1em 2em 3em;
37 | }
38 |
--------------------------------------------------------------------------------
/docs/styles/vanilla-js-tabs.css:
--------------------------------------------------------------------------------
1 | .js-tabs {
2 | margin: 2em;
3 | max-width: 100%;
4 | }
5 | .js-tabs__header {
6 | display: block;
7 | margin: 0;
8 | padding: 0;
9 | overflow: hidden;
10 | }
11 | .js-tabs__header li {
12 | display: inline-block;
13 | float: left;
14 | }
15 | .js-tabs__title {
16 | background: #f5f5f5;
17 | border: 1px solid #ccc;
18 | cursor: pointer;
19 | display: block;
20 | margin-right: 0.5em;
21 | padding: 1em 1.5em;
22 | transition: all 0.25s;
23 | }
24 | .js-tabs__title:hover {
25 | text-decoration: none;
26 | }
27 | .js-tabs__title-active {
28 | background: #fff;
29 | border-bottom-color: #fff;
30 | border-top-left-radius: 0.75em;
31 | }
32 | .js-tabs__content {
33 | border: 1px solid #ccc;
34 | line-height: 1.5;
35 | margin-top: -1px;
36 | padding: 1em 2em 3em;
37 | }
38 |
--------------------------------------------------------------------------------
/src/styles/vanilla-js-tabs.less:
--------------------------------------------------------------------------------
1 | .js-tabs {
2 | margin: 2em;
3 | max-width: 100%;
4 | }
5 |
6 | .js-tabs__header {
7 | display: block;
8 | margin: 0;
9 | padding: 0;
10 | overflow: hidden;
11 |
12 | li {
13 | display: inline-block;
14 | float: left;
15 | }
16 | }
17 |
18 | .js-tabs__title {
19 | background: #f5f5f5;
20 | border: 1px solid #ccc;
21 | cursor: pointer;
22 | display: block;
23 | margin-right: .5em;
24 | padding: 1em 1.5em;
25 | transition: all .25s;
26 |
27 | &:hover {
28 | text-decoration: none;
29 | }
30 | }
31 |
32 | .js-tabs__title-active {
33 | background: #fff;
34 | border-bottom-color: #fff;
35 | border-top-left-radius: .75em;
36 | }
37 |
38 | .js-tabs__content {
39 | border: 1px solid #ccc;
40 | line-height: 1.5;
41 | margin-top: -1px;
42 | padding: 1em 2em 3em;
43 | }
44 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | // Define globals exposed by modern browsers.
3 | "browser": true,
4 |
5 | // Define globals exposed by Node.js.
6 | "node": true,
7 |
8 | // Force all variable names to use either camelCase style or UPPER_CASE.
9 | "camelcase": true,
10 |
11 | // Prohibit use of == and != in favor of === and !==.
12 | "eqeqeq": true,
13 |
14 | // Enforce tab width of 2 spaces.
15 | "indent": 2,
16 |
17 | // Prohibit use of a variable before it is defined.
18 | "latedef": false,
19 |
20 | // Enforce line length to 100 characters
21 | "maxlen": 100,
22 |
23 | // Require capitalized names for constructor functions.
24 | "newcap": true,
25 |
26 | // Enforce use of single quotation marks for strings.
27 | "quotmark": "single",
28 |
29 | // Prohibit use of explicitly undeclared variables.
30 | "undef": true,
31 |
32 | // Warn when variables are defined but never used.
33 | "unused": true,
34 |
35 | // Suppress warnings about == null comparisons.
36 | "eqnull": true
37 | }
--------------------------------------------------------------------------------
/dist/vanilla-js-tabs.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Vanilla JavaScript Tabs v2.0.1
3 | * https://zoltantothcom.github.io/vanilla-js-tabs
4 | */
5 | const Tabs=function(e){var t=document.getElementById(e.elem);if(!t)throw new Error(`Element with ID "${e.elem}" not found`);const n=t;let l=e.open||0;const r="js-tabs__title",c="js-tabs__title-active",o="js-tabs__content",a=n.querySelectorAll("."+r).length;function s(e){n.addEventListener("click",i);var t=d(null==e?l:e);for(let e=0;e{e.style.display="none"}),[].forEach.call(n.querySelectorAll("."+r),e=>{e.className=function(e,t){t=new RegExp(`(\\s|^)${t}(\\s|$)`,"g");return e.replace(t,"")}(e.className,c)})}function d(e){return e<0||isNaN(e)||e>=a?0:e}function f(e){u();e=d(e);n.querySelectorAll("."+r)[e].classList.add(c),n.querySelectorAll("."+o)[e].style.display=""}function y(){n.removeEventListener("click",i)}return s(),{open:f,update:function(e){y(),u(),s(e)},destroy:y}};
--------------------------------------------------------------------------------
/docs/javascript/vanilla-js-tabs.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Vanilla JavaScript Tabs v2.0.1
3 | * https://zoltantothcom.github.io/vanilla-js-tabs
4 | */
5 | const Tabs=function(e){var t=document.getElementById(e.elem);if(!t)throw new Error(`Element with ID "${e.elem}" not found`);const n=t;let l=e.open||0;const r="js-tabs__title",c="js-tabs__title-active",o="js-tabs__content",a=n.querySelectorAll("."+r).length;function s(e){n.addEventListener("click",i);var t=d(null==e?l:e);for(let e=0;e {e.style.display="none"}),[].forEach.call(n.querySelectorAll("."+r),e=>{e.className=function(e,t){t=new RegExp(`(\\s|^)${t}(\\s|$)`,"g");return e.replace(t,"")}(e.className,c)})}function d(e){return e<0||isNaN(e)||e>=a?0:e}function f(e){u();e=d(e);n.querySelectorAll("."+r)[e].classList.add(c),n.querySelectorAll("."+o)[e].style.display=""}function y(){n.removeEventListener("click",i)}return s(),{open:f,update:function(e){y(),u(),s(e)},destroy:y}};
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
26 |
--------------------------------------------------------------------------------
/docs/styles/docs-page.css:
--------------------------------------------------------------------------------
1 | body{counter-reset:item;font-family:sans-serif;font-size:15px;margin:0;padding:0}img{display:inline-block}header{width:800px;margin:16px auto;text-align:center}h1{margin:48px 0 0}h3{font-size:18px;font-style:italic;font-weight:400;margin:32px 0 48px}h4{font-size:16px;font-weight:400;margin:32px 0 48px}section{border:1px solid #f0db4f;border-radius:3px;line-height:1.75;margin:0 auto 32px;width:800px}section h2{background:#fefac9;border-bottom:1px solid #f0db4f;font-size:15px;margin:0 0 30px;padding:10px}section ol,section p,section ul{margin:0 45px 30px}section ol{list-style:none;margin-left:25px}section ol li{counter-increment:item;margin-bottom:3em}section ol li:before{margin-right:10px;border-radius:4px;content:counter(item);background:#272822;color:#fff;width:2em;text-align:center;display:inline-block;height:2em;line-height:2em}section a{color:#55acee;text-decoration:none}section a:hover{text-decoration:underline}section table{border:1px solid #eee;border-collapse:collapse;font-size:14px;margin:16px 32px 32px;width:92%}section table th{background:#272822;color:#fafafa;font-size:14px;font-weight:400}section table th.subhead{background:#fffeee;color:#e09e41}section table td{font-family:monospace}section table tr:nth-child(2n){background:#f5f5f5}section table td,section table th{border:1px solid #eee;padding:10px;text-align:left}section section code{font-size:16px}.smaller{font-size:16px;font-style:italic}#custom-color-select{display:block;margin:0 auto;width:24em}input{display:block;height:3em;margin:2em auto;width:8em}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vanilla-js-tabs",
3 | "version": "2.0.1",
4 | "description": "Vanilla JavaScript tabs - extremely tiny, but gets the job done.",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "gulp",
8 | "test": "karma start karma.conf.js"
9 | },
10 | "devDependencies": {
11 | "@types/jasmine": "^5.1.4",
12 | "@types/jasmine-jquery": "^1.5.37",
13 | "@types/jquery": "^3.5.29",
14 | "coveralls": "^3.1.1",
15 | "gulp": "^4.0.2",
16 | "gulp-clean-css": "^4.3.0",
17 | "gulp-cli": "^2.3.0",
18 | "gulp-header": "^2.0.9",
19 | "gulp-jshint": "^2.1.0",
20 | "gulp-less": "^4.0.1",
21 | "gulp-pug": "^4.0.1",
22 | "gulp-rename": "^1.4.0",
23 | "gulp-strip-code": "^0.1.4",
24 | "gulp-typescript": "^6.0.0-alpha.1",
25 | "gulp-uglify": "^3.0.2",
26 | "jasmine": "^5.1.0",
27 | "jasmine-core": "^3.99.1",
28 | "jasmine-jquery": "^2.1.1",
29 | "jquery": "^3.7.1",
30 | "jshint": "^2.13.6",
31 | "jshint-stylish": "^2.2.1",
32 | "karma": "^4.4.1",
33 | "karma-chrome-launcher": "^2.2.0",
34 | "karma-cli": "^2.0.0",
35 | "karma-coverage": "^1.1.2",
36 | "karma-jasmine": "^2.0.1",
37 | "karma-jasmine-jquery": "^0.1.1",
38 | "karma-phantomjs-launcher": "^1.0.4",
39 | "karma-spec-reporter": "0.0.32",
40 | "karma-typescript": "^5.5.4",
41 | "phantom": "^6.3.0",
42 | "pug": "^2.0.4",
43 | "typescript": "^5.4.5"
44 | },
45 | "repository": {
46 | "type": "git",
47 | "url": "https://github.com/zoltantothcom/vanilla-js-tabs.git"
48 | },
49 | "author": "Zoltan Toth",
50 | "license": "Unlicense",
51 | "bugs": {
52 | "url": "https://github.com/zoltantothcom/vanilla-js-tabs/issues"
53 | },
54 | "homepage": "https://zoltantothcom.github.io/vanilla-js-tabs"
55 | }
56 |
--------------------------------------------------------------------------------
/test/spec/fixtures/tabs.fixture.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
ONE
12 |
Shabby chic ennui cred godard, forage roof party scenester health goth typewriter pitchfork. Stumptown whatever fap, austin heirloom asymmetrical lo-fi ethical seitan. Post-ironic hella listicle brunch meggings artisan. YOLO tattooed blue bottle, fanny pack gluten-free put a bird on it migas forage trust fund.
13 |
14 |
15 |
16 |
TWO
17 |
18 |
19 |
Shabby chic ennui cred godard, forage roof party scenester health goth typewriter pitchfork. Stumptown whatever fap, austin heirloom asymmetrical lo-fi ethical seitan. Post-ironic hella listicle brunch meggings artisan. YOLO tattooed blue bottle, fanny pack gluten-free put a bird on it migas forage trust fund.
20 |
21 |
22 |
23 |
THREE
24 |
25 |
26 |
27 |
28 |
FOUR
29 |
Meggings distillery pop-up artisan, hashtag 90's echo park kickstarter gluten-free. Pinterest gentrify squid vinyl chicharrones meh venmo. Beard aesthetic whatever bicycle rights artisan gastropub. Fingerstache bicycle rights you probably haven't heard of them, schlitz franzen semiotics microdosing.
30 |
Shabby chic ennui cred godard, forage roof party scenester health goth typewriter pitchfork. Stumptown whatever fap, austin heirloom asymmetrical lo-fi ethical seitan. Post-ironic hella listicle brunch meggings artisan. YOLO tattooed blue bottle, fanny pack gluten-free put a bird on it migas forage trust fund.
31 |
32 |
33 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var ts = require("gulp-typescript"),
2 | pkg = require("./package.json"),
3 | pug = require("gulp-pug"),
4 | gulp = require("gulp"),
5 | less = require("gulp-less"),
6 | clean = require("gulp-clean-css"),
7 | uglify = require("gulp-uglify"),
8 | rename = require("gulp-rename"),
9 | header = require("gulp-header"),
10 | jshint = require("gulp-jshint"),
11 | stylish = require("jshint-stylish");
12 |
13 | var banner = [
14 | "/**",
15 | " * Vanilla JavaScript Tabs v<%= pkg.version %>",
16 | " * <%= pkg.homepage %>",
17 | " */",
18 | "",
19 | ].join("\n");
20 |
21 | gulp.task("ts", function () {
22 | return gulp
23 | .src("./src/javascript/vanilla-js-tabs.ts")
24 | .pipe(
25 | ts({
26 | target: "es2015",
27 | lib: ["es2015", "dom"],
28 | noImplicitAny: true,
29 | outFile: "vanilla-js-tabs.js",
30 | })
31 | )
32 | .pipe(gulp.dest("./dist"));
33 | });
34 |
35 | gulp.task("script", function (done) {
36 | gulp
37 | .src(["./dist/vanilla-js-tabs.js"])
38 | .pipe(uglify())
39 | .pipe(
40 | header(banner, {
41 | pkg: pkg,
42 | })
43 | )
44 | .pipe(
45 | rename({
46 | suffix: ".min",
47 | })
48 | )
49 | .pipe(gulp.dest("./dist"))
50 | .pipe(gulp.dest("./docs/javascript"));
51 |
52 | done();
53 | });
54 |
55 | gulp.task("markup", function (done) {
56 | gulp
57 | .src("./src/index.pug")
58 | .pipe(
59 | pug({
60 | pretty: true,
61 | })
62 | )
63 | .pipe(gulp.dest("./dist"));
64 |
65 | done();
66 | });
67 |
68 | gulp.task("styles", function (done) {
69 | gulp
70 | .src("./src/styles/*.less")
71 | .pipe(less())
72 | .pipe(gulp.dest("./dist"))
73 | .pipe(gulp.dest("./docs/styles"));
74 |
75 | done();
76 | });
77 |
78 | gulp.task("docs-styles", function (done) {
79 | gulp
80 | .src("./docs/styles/*.less")
81 | .pipe(less())
82 | .pipe(
83 | clean({
84 | compatibility: "ie9",
85 | })
86 | )
87 | .pipe(gulp.dest("./docs/styles"));
88 |
89 | done();
90 | });
91 |
92 | gulp.task("lint", function () {
93 | return gulp
94 | .src("./src/javascript/*.js")
95 | .pipe(jshint(".jshintrc"))
96 | .pipe(jshint.reporter(stylish));
97 | });
98 |
99 | gulp.task(
100 | "default",
101 | gulp.series("ts", "script", "markup", "styles", "docs-styles", "lint")
102 | );
103 |
--------------------------------------------------------------------------------
/docs/styles/prism.css:
--------------------------------------------------------------------------------
1 | /* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript+scss */
2 | /**
3 | * okaidia theme for JavaScript, CSS and HTML
4 | * Loosely based on Monokai textmate theme by http://www.monokai.nl/
5 | * @author ocodia
6 | */
7 |
8 | code[class*="language-"],
9 | pre[class*="language-"] {
10 | color: #f8f8f2;
11 | text-shadow: 0 1px rgba(0, 0, 0, 0.3);
12 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
13 | direction: ltr;
14 | text-align: left;
15 | white-space: pre;
16 | word-spacing: normal;
17 | word-break: normal;
18 | word-wrap: normal;
19 | line-height: 1.5;
20 |
21 | -moz-tab-size: 4;
22 | -o-tab-size: 4;
23 | tab-size: 4;
24 |
25 | -webkit-hyphens: none;
26 | -moz-hyphens: none;
27 | -ms-hyphens: none;
28 | hyphens: none;
29 | }
30 |
31 | /* Code blocks */
32 | pre[class*="language-"] {
33 | padding: 1em;
34 | margin: .5em 0;
35 | overflow: auto;
36 | border-radius: 0.3em;
37 | }
38 |
39 | :not(pre) > code[class*="language-"],
40 | pre[class*="language-"] {
41 | background: #272822;
42 | }
43 |
44 | /* Inline code */
45 | :not(pre) > code[class*="language-"] {
46 | padding: .1em;
47 | border-radius: .3em;
48 | white-space: normal;
49 | }
50 |
51 | .token.comment,
52 | .token.prolog,
53 | .token.doctype,
54 | .token.cdata {
55 | color: slategray;
56 | }
57 |
58 | .token.punctuation {
59 | color: #f8f8f2;
60 | }
61 |
62 | .namespace {
63 | opacity: .7;
64 | }
65 |
66 | .token.property,
67 | .token.tag,
68 | .token.constant,
69 | .token.symbol,
70 | .token.deleted {
71 | color: #f92672;
72 | }
73 |
74 | .token.boolean,
75 | .token.number {
76 | color: #ae81ff;
77 | }
78 |
79 | .token.selector,
80 | .token.attr-name,
81 | .token.string,
82 | .token.char,
83 | .token.builtin,
84 | .token.inserted {
85 | color: #a6e22e;
86 | }
87 |
88 | .token.operator,
89 | .token.entity,
90 | .token.url,
91 | .language-css .token.string,
92 | .style .token.string,
93 | .token.variable {
94 | color: #f8f8f2;
95 | }
96 |
97 | .token.atrule,
98 | .token.attr-value,
99 | .token.function {
100 | color: #e6db74;
101 | }
102 |
103 | .token.keyword {
104 | color: #66d9ef;
105 | }
106 |
107 | .token.regex,
108 | .token.important {
109 | color: #fd971f;
110 | }
111 |
112 | .token.important,
113 | .token.bold {
114 | font-weight: bold;
115 | }
116 | .token.italic {
117 | font-style: italic;
118 | }
119 |
120 | .token.entity {
121 | cursor: help;
122 | }
123 |
124 |
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Vanilla JavaScript Dropdown
6 |
7 |
8 |
9 |
10 |
16 |
17 |
ONE
18 |
Shabby chic ennui cred godard, forage roof party scenester health goth typewriter pitchfork. Stumptown whatever fap, austin heirloom asymmetrical lo-fi ethical seitan. Post-ironic hella listicle brunch meggings artisan. YOLO tattooed blue bottle, fanny pack gluten-free put a bird on it migas forage trust fund.
19 |
20 |
21 |
TWO
22 |
23 | Shabby chic cred godard, forage roof party scenester health goth typewriter pitchfork. Stumptown whatever fap, austin heirloom asymmetrical lo-fi ethical seitan. Post-ironic hella listicle brunch meggings artisan. YOLO tattooed blue bottle, fanny pack gluten-free put a bird on it migas forage trust fund.
24 |
25 |
26 |
27 |
28 |
THREE
29 |
30 |
31 |
FOUR
32 |
Meggings distillery pop-up artisan, hashtag 90's echo park kickstarter gluten-free. Pinterest gentrify squid vinyl chicharrones meh venmo. Beard aesthetic whatever bicycle rights artisan gastropub. Fingerstache bicycle rights you probably haven't heard of them, schlitz franzen semiotics microdosing.
33 |
Shabby chic ennui cred godard, forage roof party scenester health goth typewriter pitchfork. Stumptown whatever fap, austin heirloom asymmetrical lo-fi ethical seitan. Post-ironic hella listicle brunch meggings artisan. YOLO tattooed blue bottle, fanny pack gluten-free put a bird on it migas forage trust fund.
34 |
35 |
36 |
37 |
38 |
44 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Tue Feb 21 2017 14:28:05 GMT-0500 (Eastern Standard Time)
3 |
4 | module.exports = function (config) {
5 | config.set({
6 | // base path that will be used to resolve all patterns (eg. files, exclude)
7 | basePath: "",
8 |
9 | // frameworks to use
10 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
11 | frameworks: ["jasmine-jquery", "jasmine", "karma-typescript"],
12 |
13 | // list of files / patterns to load in the browser
14 | files: [
15 | "src/*/*.ts",
16 | "test/*js",
17 | "dist/vanilla-js-tabs.js",
18 | "test/spec/*.ts",
19 | "node_modules/jquery/dist/jquery.min.js",
20 | "test/spec/fixtures/*.html",
21 | {
22 | pattern: "img/*.jpg",
23 | watched: false,
24 | included: false,
25 | served: true,
26 | nocache: false,
27 | },
28 | ],
29 |
30 | karmaTypescriptConfig: {
31 | transformPath: function (filepath) {
32 | return filepath.replace(/\.(ts|tsx)$/, ".js");
33 | },
34 | },
35 |
36 | // list of files to exclude
37 | exclude: [],
38 |
39 | // preprocess matching files before serving them to the browser
40 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
41 | preprocessors: {
42 | "src/*/*.js": "coverage",
43 | "src/**/*.ts": ["karma-typescript"],
44 | "test/**/*.spec.ts": ["karma-typescript"],
45 | },
46 |
47 | // test results reporter to use
48 | // possible values: 'dots', 'progress'
49 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter
50 | reporters: ["spec", "coverage", "karma-typescript"],
51 | coverageReporter: {
52 | type: "lcov",
53 | dir: "coverage",
54 | },
55 |
56 | // web server port
57 | port: 9876,
58 |
59 | // enable / disable colors in the output (reporters and logs)
60 | colors: true,
61 |
62 | // level of logging
63 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
64 | logLevel: config.LOG_INFO,
65 |
66 | // enable / disable watching file and executing tests whenever any file changes
67 | autoWatch: true,
68 |
69 | // start these browsers
70 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
71 | browsers: ["Chrome"],
72 |
73 | // Continuous Integration mode
74 | // if true, Karma captures browsers, runs the tests and exits
75 | singleRun: true,
76 | });
77 | };
78 |
--------------------------------------------------------------------------------
/src/index.pug:
--------------------------------------------------------------------------------
1 | doctype html
2 | html(lang="en")
3 | head
4 | meta(charset="utf-8")
5 |
6 | title="Vanilla JavaScript Dropdown"
7 | link(rel="stylesheet" href="vanilla-js-dropdown.css")
8 |
9 | body
10 |
11 | div.js-tabs#tabs
12 |
13 | ul.js-tabs__header
14 | li
15 | a.js-tabs__title(href="#") Title 1
16 | li
17 | a.js-tabs__title(href="#") Title 2
18 | li
19 | a.js-tabs__title(href="#") Title 3
20 | li
21 | a.js-tabs__title(href="#") Title 4
22 |
23 |
24 | div.js-tabs__content
25 | h1 ONE
26 | p Shabby chic ennui cred godard, forage roof party scenester health goth typewriter pitchfork. Stumptown whatever fap, austin heirloom asymmetrical lo-fi ethical seitan. Post-ironic hella listicle brunch meggings artisan. YOLO tattooed blue bottle, fanny pack gluten-free put a bird on it migas forage trust fund.
27 |
28 | div.js-tabs__content
29 | h1 TWO
30 | img(src="http://lorempixel.com/600/100" alt="")
31 |
32 | p.
33 | Shabby chic cred godard, forage roof party scenester health goth typewriter pitchfork. Stumptown whatever fap, austin heirloom asymmetrical lo-fi ethical seitan. Post-ironic hella listicle brunch meggings artisan. YOLO tattooed blue bottle, fanny pack gluten-free put a bird on it migas forage trust fund.
34 |
35 | div.js-tabs__content
36 | h1 THREE
37 | img(src="http://lorempixel.com/500/300" alt="")
38 |
39 | div.js-tabs__content
40 | h1 FOUR
41 | p Meggings distillery pop-up artisan, hashtag 90's echo park kickstarter gluten-free. Pinterest gentrify squid vinyl chicharrones meh venmo. Beard aesthetic whatever bicycle rights artisan gastropub. Fingerstache bicycle rights you probably haven't heard of them, schlitz franzen semiotics microdosing.
42 | p Shabby chic ennui cred godard, forage roof party scenester health goth typewriter pitchfork. Stumptown whatever fap, austin heirloom asymmetrical lo-fi ethical seitan. Post-ironic hella listicle brunch meggings artisan. YOLO tattooed blue bottle, fanny pack gluten-free put a bird on it migas forage trust fund.
43 |
44 | script(src="vanilla-js-tabs.min.js")
45 | script.
46 | var tabs = Tabs({
47 | elem: 'tabs',
48 | open: 1
49 | });
50 |
--------------------------------------------------------------------------------
/docs/styles/docs-page.less:
--------------------------------------------------------------------------------
1 | body {
2 | counter-reset: item;
3 | font-family: sans-serif;
4 | font-size: 15px;
5 | margin: 0;
6 | padding: 0;
7 | }
8 |
9 | img {
10 | display: inline-block;
11 | }
12 |
13 | header {
14 | width: 800px;
15 | margin: 16px auto;
16 | text-align: center;
17 | }
18 |
19 | h1 {
20 | margin: 48px 0 0;
21 | }
22 |
23 | h3 {
24 | font-size: 18px;
25 | font-style: italic;
26 | font-weight: 400;
27 | margin: 32px 0 48px;
28 | }
29 |
30 | h4 {
31 | font-size: 16px;
32 | font-weight: 400;
33 | margin: 32px 0 48px;
34 | }
35 |
36 | section {
37 | border: 1px solid #f0db4f;
38 | border-radius: 3px;
39 | line-height: 1.75;
40 | margin: 0 auto 32px;
41 | width: 800px;
42 |
43 | h2 {
44 | background: #fefac9;
45 | border-bottom: 1px solid #f0db4f;
46 | font-size: 15px;
47 | margin: 0 0 30px;
48 | padding: 10px;
49 | }
50 |
51 | p,
52 | ul,
53 | ol {
54 | margin: 0 45px 30px;
55 | }
56 |
57 | ol {
58 | list-style: none;
59 | margin-left: 25px;
60 |
61 | li {
62 | counter-increment: item;
63 | margin-bottom: 3em;
64 |
65 | &:before {
66 | margin-right: 10px;
67 | border-radius: 4px;
68 | content: counter(item);
69 | background: #272822;
70 | color: #fff;
71 | width: 2em;
72 | text-align: center;
73 | display: inline-block;
74 | height: 2em;
75 | line-height: 2em;
76 | }
77 | }
78 | }
79 |
80 | a {
81 | color: #55acee;
82 | text-decoration: none;
83 |
84 | &:hover {
85 | text-decoration: underline;
86 | }
87 | }
88 |
89 | table {
90 | border: 1px solid #eee;
91 | border-collapse: collapse;
92 | font-size: 14px;
93 | margin: 16px 32px 32px;
94 | width: 92%;
95 |
96 | th {
97 | background: #272822;
98 | color: #fafafa;
99 | font-size: 14px;
100 | font-weight: 400;
101 |
102 | &.subhead {
103 | background: #fffeee;
104 | color: #e09e41;
105 | }
106 | }
107 |
108 | td {
109 | font-family: monospace;
110 | }
111 |
112 | tr:nth-child(2n) {
113 | background: #f5f5f5;
114 | }
115 |
116 | th,
117 | td {
118 | border: 1px solid #eee;
119 | padding: 10px;
120 | text-align: left;
121 | }
122 | }
123 |
124 | section code {
125 | font-size: 16px;
126 | }
127 | }
128 |
129 | .smaller {
130 | font-size: 16px;
131 | font-style: italic;
132 | }
133 |
134 | #custom-color-select {
135 | display: block;
136 | margin: 0 auto;
137 | width: 24em;
138 | }
139 |
140 | input {
141 | display: block;
142 | height: 3em;
143 | margin: 2em auto;
144 | width: 8em;
145 | }
146 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vanilla JavaScript Tabs
2 |
3 | [](https://coveralls.io/github/zoltantothcom/vanilla-js-tabs?branch=master) 
4 |
5 | Vanilla JavaScript Tabs - simple and awesome.
6 |
7 | _— Inspired by the blazing fast, lightweight, cross-platform and crazy popular [Vanilla JS](http://vanilla-js.com/) framework._
8 |
9 | ## Demo
10 |
11 | [TABS](http://zoltantothcom.github.io/vanilla-js-tabs)
12 |
13 | ## Options
14 |
15 | | Option | Type | Default | Description |
16 | | ------ | ------ | ------- | -------------------------------------------------- |
17 | | elem | string | | HTML _id_ of the tab container in the HTML markup. |
18 | | open | number | 0 | Opens this tab initially. |
19 |
20 | ## Methods
21 |
22 | | Method | Type | Description |
23 | | --------- | ------ | ---------------------------------------------------------------------------------- |
24 | | open(n) | number | Opens a tab by index |
25 | | update(n) | number | Updates the tabs with _n_-th tab open _(useful when dynamically adding tabs)_ |
26 | | destroy() | | Removes the listeners |
27 |
28 | ## Usage example
29 |
30 | ```javascript
31 | var tabs = Tabs({
32 | elem: "tabs",
33 | open: 1,
34 | });
35 | ```
36 |
37 | ```javascript
38 | // Open any other tab
39 | tabs.open(3);
40 | ```
41 |
42 | ## Running the tests
43 |
44 | ```
45 | npm test
46 | ```
47 |
48 | ## Browser support and dependencies
49 |
50 | | Browser | Support | Dependencies |
51 | | ---------- | ------- | ------------ |
52 | | Chrome | yes | - |
53 | | Firefox | yes | - |
54 | | Safari | yes | - |
55 | | Opera | yes | - |
56 | | IE9 and up | yes | - |
57 |
58 | ## License
59 |
60 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
61 |
62 | See [Unlicense](http://unlicense.org) for full details.
63 |
64 | ## Related
65 |
66 | - [Vanilla JavaScript **Carousel**](https://github.com/zoltantothcom/vanilla-js-carousel)
67 | - [Vanilla JavaScript **Dropdown**](https://github.com/zoltantothcom/vanilla-js-dropdown)
68 | - [Vanilla JavaScript **Tooltip**](https://github.com/zoltantothcom/vanilla-js-tooltip)
69 | - [Vanilla JavaScript **Accordion**](https://github.com/zoltantothcom/vanilla-js-accordion)
70 |
--------------------------------------------------------------------------------
/dist/vanilla-js-tabs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | * @author Zoltan Toth
4 | * @version 2.0.1
5 | */
6 | const Tabs = function (options) {
7 | const el = document.getElementById(options.elem);
8 | if (!el)
9 | throw new Error(`Element with ID "${options.elem}" not found`);
10 | const elem = el;
11 | let open = options.open || 0;
12 | const titleClass = "js-tabs__title";
13 | const activeClass = "js-tabs__title-active";
14 | const contentClass = "js-tabs__content";
15 | const tabsNum = elem.querySelectorAll(`.${titleClass}`).length;
16 | render();
17 | /**
18 | * Initial rendering of the tabs.
19 | */
20 | function render(n) {
21 | elem.addEventListener("click", onClick);
22 | const init = n == null ? checkTab(open) : checkTab(n);
23 | for (let i = 0; i < tabsNum; i++) {
24 | elem.querySelectorAll(`.${titleClass}`)[i].setAttribute("data-index", i.toString());
25 | if (i === init)
26 | openTab(i);
27 | }
28 | }
29 | /**
30 | * Handle clicks on the tabs.
31 | *
32 | * @param {object} e - Element the click occured on.
33 | */
34 | function onClick(e) {
35 | var _a;
36 | const target = e.target.closest(`.${titleClass}`);
37 | if (!target)
38 | return;
39 | e.preventDefault();
40 | openTab(parseInt((_a = target.getAttribute("data-index")) !== null && _a !== void 0 ? _a : "0"));
41 | }
42 | /**
43 | * Hide all tabs and re-set tab titles.
44 | */
45 | function reset() {
46 | [].forEach.call(elem.querySelectorAll(`.${contentClass}`), (item) => {
47 | item.style.display = "none";
48 | });
49 | [].forEach.call(elem.querySelectorAll(`.${titleClass}`), (item) => {
50 | item.className = removeClass(item.className, activeClass);
51 | });
52 | }
53 | /**
54 | * Utility function to remove the open class from tab titles.
55 | *
56 | * @param {string} str - Current class.
57 | * @param {string} cls - The class to remove.
58 | */
59 | function removeClass(str, cls) {
60 | const reg = new RegExp(`(\\s|^)${cls}(\\s|$)`, "g");
61 | return str.replace(reg, "");
62 | }
63 | /**
64 | * Utility function to remove the open class from tab titles.
65 | *
66 | * @param n - Tab to open.
67 | */
68 | function checkTab(n) {
69 | return n < 0 || isNaN(n) || n >= tabsNum ? 0 : n;
70 | }
71 | /**
72 | * Opens a tab by index.
73 | *
74 | * @param {number} n - Index of tab to open. Starts at 0.
75 | *
76 | * @public
77 | */
78 | function openTab(n) {
79 | reset();
80 | const i = checkTab(n);
81 | elem.querySelectorAll(`.${titleClass}`)[i].classList.add(activeClass);
82 | elem.querySelectorAll(`.${contentClass}`)[i].style.display = "";
83 | }
84 | /**
85 | * Updates the tabs.
86 | *
87 | * @param {number} n - Index of tab to open. Starts at 0.
88 | *
89 | * @public
90 | */
91 | function update(n) {
92 | destroy();
93 | reset();
94 | render(n);
95 | }
96 | /**
97 | * Removes the listeners from the tabs.
98 | *
99 | * @public
100 | */
101 | function destroy() {
102 | elem.removeEventListener("click", onClick);
103 | }
104 | return {
105 | open: openTab,
106 | update,
107 | destroy,
108 | };
109 | };
110 |
--------------------------------------------------------------------------------
/src/javascript/vanilla-js-tabs.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @fileOverview
3 | * @author Zoltan Toth
4 | * @version 2.0.1
5 | */
6 |
7 | /**
8 | * @description
9 | * Vanilla Javascript Tabs
10 | *
11 | * @class
12 | * @param {string} options.elem - HTML id of the tabs container
13 | * @param {number} [options.open = 0] - Render the tabs with this item open
14 | */
15 |
16 | interface TabsOptions {
17 | elem: string;
18 | open?: number;
19 | }
20 |
21 | interface Tabs {
22 | open: (n: number) => void;
23 | update: (n: number) => void;
24 | destroy: () => void;
25 | }
26 |
27 | const Tabs = function (options: TabsOptions): Tabs {
28 | const el: HTMLElement | null = document.getElementById(options.elem);
29 | if (!el) throw new Error(`Element with ID "${options.elem}" not found`);
30 |
31 | const elem = el;
32 |
33 | let open: number = options.open || 0;
34 | const titleClass: string = "js-tabs__title";
35 | const activeClass: string = "js-tabs__title-active";
36 | const contentClass: string = "js-tabs__content";
37 | const tabsNum: number = elem.querySelectorAll(`.${titleClass}`).length;
38 |
39 | render();
40 |
41 | /**
42 | * Initial rendering of the tabs.
43 | */
44 | function render(n?: number): void {
45 | elem.addEventListener("click", onClick);
46 |
47 | const init = n == null ? checkTab(open) : checkTab(n);
48 |
49 | for (let i = 0; i < tabsNum; i++) {
50 | (elem.querySelectorAll(`.${titleClass}`)[i] as HTMLElement).setAttribute(
51 | "data-index",
52 | i.toString()
53 | );
54 | if (i === init) openTab(i);
55 | }
56 | }
57 |
58 | /**
59 | * Handle clicks on the tabs.
60 | *
61 | * @param {object} e - Element the click occured on.
62 | */
63 | function onClick(e: MouseEvent): void {
64 | const target = (e.target as HTMLElement).closest(`.${titleClass}`);
65 | if (!target) return;
66 |
67 | e.preventDefault();
68 |
69 | openTab(parseInt(target.getAttribute("data-index") ?? "0"));
70 | }
71 |
72 | /**
73 | * Hide all tabs and re-set tab titles.
74 | */
75 | function reset(): void {
76 | [].forEach.call(
77 | elem.querySelectorAll(`.${contentClass}`),
78 | (item: HTMLElement) => {
79 | item.style.display = "none";
80 | }
81 | );
82 |
83 | [].forEach.call(
84 | elem.querySelectorAll(`.${titleClass}`),
85 | (item: HTMLElement) => {
86 | item.className = removeClass(item.className, activeClass);
87 | }
88 | );
89 | }
90 |
91 | /**
92 | * Utility function to remove the open class from tab titles.
93 | *
94 | * @param {string} str - Current class.
95 | * @param {string} cls - The class to remove.
96 | */
97 | function removeClass(str: string, cls: string): string {
98 | const reg = new RegExp(`(\\s|^)${cls}(\\s|$)`, "g");
99 | return str.replace(reg, "");
100 | }
101 |
102 | /**
103 | * Utility function to remove the open class from tab titles.
104 | *
105 | * @param n - Tab to open.
106 | */
107 | function checkTab(n: number): number {
108 | return n < 0 || isNaN(n) || n >= tabsNum ? 0 : n;
109 | }
110 |
111 | /**
112 | * Opens a tab by index.
113 | *
114 | * @param {number} n - Index of tab to open. Starts at 0.
115 | *
116 | * @public
117 | */
118 | function openTab(n: number): void {
119 | reset();
120 |
121 | const i = checkTab(n);
122 |
123 | elem.querySelectorAll(`.${titleClass}`)[i].classList.add(activeClass);
124 | (
125 | elem.querySelectorAll(`.${contentClass}`)[i] as HTMLElement
126 | ).style.display = "";
127 | }
128 |
129 | /**
130 | * Updates the tabs.
131 | *
132 | * @param {number} n - Index of tab to open. Starts at 0.
133 | *
134 | * @public
135 | */
136 | function update(n: number): void {
137 | destroy();
138 | reset();
139 | render(n);
140 | }
141 |
142 | /**
143 | * Removes the listeners from the tabs.
144 | *
145 | * @public
146 | */
147 | function destroy(): void {
148 | elem.removeEventListener("click", onClick);
149 | }
150 |
151 | return {
152 | open: openTab,
153 | update,
154 | destroy,
155 | };
156 | };
157 |
--------------------------------------------------------------------------------
/test/spec/tabs.spec.ts:
--------------------------------------------------------------------------------
1 | const fixturePath: string = "base/test/spec/fixtures";
2 | const tabsFixture: string = "tabs.fixture.html";
3 |
4 | interface Tabs {
5 | open: (n: number) => void;
6 | update: (n: number) => void;
7 | destroy: () => void;
8 | }
9 |
10 | describe("TABS", function () {
11 | beforeEach(function () {
12 | jasmine.getFixtures().fixturesPath = fixturePath;
13 | loadFixtures(tabsFixture);
14 |
15 | const tabsInstance = Tabs({
16 | elem: "tabs",
17 | open: -123,
18 | }) as Tabs;
19 |
20 | this.tabs = tabsInstance;
21 | });
22 |
23 | afterEach(function () {
24 | this.tabs.destroy();
25 | });
26 |
27 | describe("original tabs", function () {
28 | it("markup should be present", function () {
29 | expect($("#tabs")).toBeDefined();
30 | });
31 |
32 | it("should have more than 0 tabs", function () {
33 | expect($(".js-tabs__title").length).toBeGreaterThan(0);
34 | expect($(".js-tabs__content").length).toBeGreaterThan(0);
35 | });
36 |
37 | it("should have the same number of titles and content blocks", function () {
38 | const titles: number = $(".js-tabs__title").length;
39 | const contents: number = $(".js-tabs__content").length;
40 |
41 | expect(titles).toBe(contents);
42 | });
43 |
44 | it("should default to 1st tab when open property is invalid", function () {
45 | expect($(".js-tabs__title")[0]).toHaveClass("js-tabs__title-active");
46 | });
47 | });
48 |
49 | describe("methods", function () {
50 | it("should have .open() method", function () {
51 | expect(typeof this.tabs.open).toBe("function");
52 | });
53 |
54 | it("should have .update() method", function () {
55 | expect(typeof this.tabs.update).toBe("function");
56 | });
57 |
58 | it("should have .destroy() method", function () {
59 | expect(typeof this.tabs.destroy).toBe("function");
60 | });
61 | });
62 |
63 | describe("method calls", function () {
64 | beforeEach(function () {
65 | jasmine.getFixtures().fixturesPath = fixturePath;
66 | loadFixtures(tabsFixture);
67 |
68 | const tabsInstance: Tabs = Tabs({
69 | elem: "tabs",
70 | });
71 |
72 | this.tabs = tabsInstance;
73 | });
74 |
75 | it("should default to 1st tab when .open() argument is invalid", function () {
76 | this.tabs.open(-123);
77 | expect($(".js-tabs__title")[0]).toHaveClass("js-tabs__title-active");
78 | });
79 |
80 | it(".open(2) should open the 3rd tab", function () {
81 | expect($(".js-tabs__title")[2]).not.toHaveClass("js-tabs__title-active");
82 | this.tabs.open(2);
83 | expect($(".js-tabs__title")[2]).toHaveClass("js-tabs__title-active");
84 | });
85 |
86 | it(".update(2) should reset the tabs with 3rd tab open", function () {
87 | expect($(".js-tabs__title")[0]).toHaveClass("js-tabs__title-active");
88 | expect($(".js-tabs__title")[2]).not.toHaveClass("js-tabs__title-active");
89 | this.tabs.update(2);
90 | expect($(".js-tabs__title")[2]).toHaveClass("js-tabs__title-active");
91 | });
92 |
93 | it("should not react to clicks after destroy()", function () {
94 | expect($(".js-tabs__title")[0]).toHaveClass("js-tabs__title-active");
95 |
96 | this.tabs.destroy();
97 |
98 | const spyEvent = spyOnEvent(".js-tabs__title", "click");
99 | $(".js-tabs__title")[1].click();
100 |
101 | expect("click").toHaveBeenTriggeredOn(".js-tabs__title");
102 | expect(spyEvent).toHaveBeenTriggered();
103 |
104 | expect($(".js-tabs__title")[1]).not.toHaveClass("js-tabs__title-active");
105 | });
106 | });
107 |
108 | describe("behavior", function () {
109 | it("should open the 2nd tab on title click", function () {
110 | expect($(".js-tabs__title")[1]).not.toHaveClass("js-tabs__title-active");
111 |
112 | const spyEvent = spyOnEvent(".js-tabs__title", "click");
113 | $(".js-tabs__title")[1].click();
114 |
115 | expect("click").toHaveBeenTriggeredOn(".js-tabs__title");
116 | expect(spyEvent).toHaveBeenTriggered();
117 |
118 | expect($(".js-tabs__title")[1]).toHaveClass("js-tabs__title-active");
119 | });
120 |
121 | it("should ignore any clicks in the content blocks", function () {
122 | this.tabs.open(2);
123 | expect($(".js-tabs__title")[2]).toHaveClass("js-tabs__title-active");
124 |
125 | const spyEvent = spyOnEvent(".js-tabs__content", "click");
126 | $(".js-tabs__content")[2].click();
127 |
128 | expect("click").toHaveBeenTriggeredOn(".js-tabs__content");
129 | expect(spyEvent).toHaveBeenTriggered();
130 |
131 | expect($(".js-tabs__title")[2]).toHaveClass("js-tabs__title-active");
132 | });
133 | });
134 | });
135 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Vanilla JavaScript tabs - a tiny select tag replacement.
7 |
8 |
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 | Vanilla JavaScript Tabs
20 |
21 | Tiny and awesome
22 |
23 |
24 |
25 | Demo
26 |
27 |
28 |
34 |
35 |
36 |
TAB 1
37 |
38 | Shabby chic ennui cred godard, forage roof party scenester health
39 | goth typewriter pitchfork.
40 |
41 |
42 |
43 |
44 |
TAB 2
45 |
46 | Shabby chic
47 | github cred godard,
48 | forage roof party scenester health goth typewriter pitchfork.
49 |
50 |
51 |
52 |
53 |
TAB 3
54 |
55 | Yolo tattooed blue bottle, fanny pack gluten-free put a bird on it
56 | migas forage trust fund.
57 |
58 |
59 |
60 |
61 |
TAB 4
62 |
63 | Fingerstache bicycle rights you probably haven't heard of them,
64 | schlitz franzen semiotics.
65 |
66 |
67 |
68 |
69 |
70 |
71 | Download
72 |
73 |
74 | Available on
75 | GitHub
80 |
81 |
82 |
83 |
84 | Installation
85 |
86 |
87 |
88 | Include the script
89 | <script src="path/to/vanilla-js-tabs.min.js"></script>
90 |
91 |
92 | Include the CSS (feel free to edit it or write your own )
93 | <link rel="stylesheet" href="path/to/vanilla-js-tabs.css">
94 |
95 |
96 | Write your tabs markup
97 |
98 | <div class="js-tabs" id="tabs">
99 |
100 | <ul class="js-tabs__header">
101 | <li><a href="#" class="js-tabs__title">Title 1</a></li>
102 | <li><a href="#" class="js-tabs__title">Title 2</a></li>
103 | <li><a href="#" class="js-tabs__title">Title 3</a></li>
104 | <li><a href="#" class="js-tabs__title">Title 4</a></li>
105 | </ul>
106 |
107 | <div class="js-tabs__content">
108 | <h1>ONE</h1>
109 | </div>
110 |
111 | <div class="js-tabs__content">
112 | <h1>TWO</h1>
113 | </div>
114 |
115 | <div class="js-tabs__content">
116 | <h1>THREE</h1>
117 | </div>
118 |
119 | <div class="js-tabs__content">
120 | <h1>FOUR</h1>
121 | </div>
122 |
123 | </div>
124 |
125 |
126 |
127 | Initialize the tabs
128 | var tabs = Tabs({
129 | elem: "tabs",
130 | open: 2
131 | });
132 |
133 | // Open any other tab
134 | tabs.open(4);
135 |
136 |
137 |
138 |
139 |
140 | Options
141 |
142 |
143 |
144 | Option
145 | Type
146 | Default
147 | Description
148 |
149 |
150 | elem
151 | string
152 |
153 | HTML id of the tab container
154 |
155 |
156 | open
157 | number
158 | 0
159 | Starts with this tab oipen
160 |
161 |
162 |
163 |
164 |
165 | Methods
166 |
167 |
168 |
169 | Method
170 | Description
171 |
172 |
173 | .open(n)
174 | Opens a tab by index
175 |
176 |
177 | .update(n)
178 |
179 | Updates tabs with n -th tab open
180 | (useful when dynamically adding tabs)
181 |
182 |
183 |
184 | .destroy()
185 | Removes the listeners
186 |
187 |
188 |
189 |
190 |
191 | Licence
192 |
193 |
194 |
195 | This is free and unencumbered software released into the public
196 | domain.
197 |
198 |
199 |
200 | Anyone is free to copy, modify, publish, use, compile, sell, or
201 | distribute this software, either in source code form or as a compiled
202 | binary, for any purpose, commercial or non-commercial, and by any
203 | means.
204 |
205 |
206 |
207 | In jurisdictions that recognize copyright laws, the author or authors
208 | of this software dedicate any and all copyright interest in the
209 | software to the public domain. We make this dedication for the benefit
210 | of the public at large and to the detriment of our heirs and
211 | successors. We intend this dedication to be an overt act of
212 | relinquishment in perpetuity of all present and future rights to this
213 | software under copyright law.
214 |
215 |
216 |
217 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
218 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
219 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
220 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
221 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
222 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
223 | OTHER DEALINGS IN THE SOFTWARE.
224 |
225 |
226 |
227 | For more information, please refer to
228 | http://unlicense.org
231 |
232 |
233 |
234 |
235 |
247 |
248 |
249 |
250 |
256 |
257 |
258 |
--------------------------------------------------------------------------------
/docs/javascript/prism.js:
--------------------------------------------------------------------------------
1 | /* http://prismjs.com/download.html?themes=prism-okaidia&languages=markup+css+clike+javascript+scss */
2 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=_self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),P=[p,1];b&&P.push(b);var A=new a(i,g?t.tokenize(m,g):m,h);P.push(A),w&&P.push(w),Array.prototype.splice.apply(r,P)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var l={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}t.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+""+l.tag+">"},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code,l=n.immediateClose;_self.postMessage(t.highlight(r,t.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
3 | Prism.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=.$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup;
4 | Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/(