├── examples ├── images │ ├── sample-image.png │ ├── sample-image1.jpg │ └── sample-image2.jpg ├── css │ ├── style.css │ └── bootstrap.min.css ├── basic_usage.html ├── advanced_usage.html └── js │ └── jquery.min.js ├── package.json ├── jquery.hotspot.css ├── README.md └── jquery.hotspot.js /examples/images/sample-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aniruddhanath/jquery-hotspot/HEAD/examples/images/sample-image.png -------------------------------------------------------------------------------- /examples/images/sample-image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aniruddhanath/jquery-hotspot/HEAD/examples/images/sample-image1.jpg -------------------------------------------------------------------------------- /examples/images/sample-image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aniruddhanath/jquery-hotspot/HEAD/examples/images/sample-image2.jpg -------------------------------------------------------------------------------- /examples/css/style.css: -------------------------------------------------------------------------------- 1 | #theElement-0, 2 | #theElement-a, 3 | #theElement-b { 4 | position: relative; 5 | } 6 | 7 | img { 8 | width: 100%; 9 | } 10 | 11 | body { 12 | background: #efefef; 13 | } 14 | 15 | .container { 16 | margin-top: 20px; 17 | } 18 | 19 | .panel-body { 20 | padding: 15px 15px 50px; 21 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-hotspot", 3 | "version": "2.0.3", 4 | "description": "jQuery plugin to create editable Hotspots on any HTML elements", 5 | "main": "jquery.hotspot.js", 6 | "directories": { 7 | "example": "examples" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/aniruddhanath/jquery-hotspot.git" 15 | }, 16 | "keywords": [ 17 | "jquery-plugin", 18 | "ecosystem:jquery", 19 | "jquery-hotspot" 20 | ], 21 | "author": "Aniruddha Nath", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/aniruddhanath/jquery-hotspot/issues" 25 | }, 26 | "homepage": "http://aniruddhanath.github.io/jquery-hotspot" 27 | } 28 | -------------------------------------------------------------------------------- /examples/basic_usage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Hotspot | Basic usage 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 |
16 |

Example - Display mode

17 |
18 |
19 |

Click the Spots!

20 |
21 | 22 |
23 |
24 |
25 | 26 |
27 | 28 | 29 | 30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /examples/advanced_usage.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Hotspot | Advanced usage 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 |
16 |

Example - Admin mode

17 |
18 |
19 |
20 | interactivity: `click` 21 |
22 | 23 |
24 |
25 |
26 | interactivity: `hover` 27 |
28 | 29 |
30 |
31 |
32 |
33 | 34 |
35 | 36 | 37 | 38 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /jquery.hotspot.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Hotspot 3 | * 4 | */ 5 | .HotspotPlugin_Hotspot { 6 | height: 20px; 7 | width: 20px; 8 | position: absolute; 9 | background: #1ABC9C; 10 | border-radius: 50%; 11 | cursor: pointer; 12 | z-index: 200; 13 | margin-left: -10px; 14 | margin-top: -10px; 15 | } 16 | 17 | /* 18 | * Hotspot content 19 | * 20 | */ 21 | .HotspotPlugin_Hotspot > div { 22 | background: rgba(26, 188, 156, 0.4); 23 | width: 140px; 24 | height: 94px; 25 | margin: -94px -60px; 26 | border-radius: 4px; 27 | overflow: hidden; 28 | font-size: 10px; 29 | display:none; /* Required */ 30 | } 31 | .HotspotPlugin_Hotspot:hover > div { 32 | display: block; /* Required */ 33 | } 34 | .HotspotPlugin_Hotspot > div > .Hotspot_Title { 35 | background: rgba(255, 255, 255, 0.4); 36 | height: 20px; 37 | font-weight: bold; 38 | padding: 4px 10px; 39 | } 40 | .HotspotPlugin_Hotspot > div > .Hotspot_Message { 41 | background: rgba(255, 255, 255, 0.4); 42 | margin-top: 2px; 43 | padding: 4px 10px; 44 | height: 72px; 45 | overflow-y: auto; 46 | } 47 | .HotspotPlugin_Hotspot_Hidden { 48 | display: none!important; 49 | visibility: hidden!important; 50 | } 51 | 52 | /* 53 | * Hotspots immediately after creation - admin-mode 54 | * 55 | */ 56 | .HotspotPlugin_Hotspot_Unsaved { 57 | background: #4E6FF3; 58 | } 59 | 60 | /* 61 | * Overlay used in the admin-mode 62 | * 63 | */ 64 | span.HotspotPlugin_Overlay { 65 | position: absolute; 66 | background-color: rgba(0, 0, 0, 0.4); 67 | top: 0px; 68 | left: 0px; 69 | cursor: pointer; 70 | } 71 | span.HotspotPlugin_Overlay > p { 72 | color: #ffffff; 73 | background: rgba(255, 255, 255, 0.4); 74 | margin-top: 0px; 75 | padding: 20px; 76 | text-align: center; 77 | } 78 | 79 | /* 80 | * Action buttons - `admin` mode 81 | * 82 | */ 83 | button.HotspotPlugin_Save, 84 | button.HotspotPlugin_Remove, 85 | button.HotspotPlugin_Send { 86 | position: absolute; 87 | bottom: -35px; 88 | color: #fff; 89 | display: inline-block; 90 | padding: 4px 6px; 91 | font-size: 14px; 92 | text-align: center; 93 | border-radius: 4px; 94 | } 95 | 96 | button.HotspotPlugin_Save { 97 | left: 0px; 98 | background-color: #5cb85c; 99 | } 100 | button.HotspotPlugin_Remove { 101 | left: 80px; 102 | background-color: #d9534f; 103 | } 104 | button.HotspotPlugin_Send { 105 | left: 180px; 106 | background-color: #5bc0de; 107 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jQuery - Hotspot 2 | === 3 | 4 | A [jQuery](http://www.jquery.com) plugin for creating *Hotspots* in an HTML element. This plugin operates in two modes, `admin` and `display`. The **admin mode** enables user to create hotspots on desired HTML elements. The **display mode** is used for only displaying the hotspots to the end user. The design of the hotspot created are fully customizable. User can add their own CSS class to style the hotspots. 5 | 6 | ### [View Demo](http://aniruddhanath.github.io/jquery-hotspot/) 7 | 8 | Note: Ensures responsiveness with the latest update. 9 | 10 | The Setup 11 | --------- 12 | 13 | ``` 14 | npm install jquery-hotspot 15 | ``` 16 | 17 | Include `jquery.hotspot.js` at the bottom of the HTML page before the closing body tag. Wrap the HTML element for which the hotspot is to be obtained with another element. Also include the `jquery.hotspot.css` file to go with some of the default design decisions. 18 | ``` 19 |
20 | 21 |
22 | ``` 23 | The plugin instance will be called with `theElement`. 24 | > **Note:** The CSS property, *position* for this element should be kept as *relative*. 25 | ``` 26 | #theElement { 27 | position: relative; 28 | } 29 | ``` 30 | 31 | 32 | Usage - Display mode 33 | -------------------- 34 | 35 | The data associated with the hotspots can be passed in directly to the plugin. The data can also be fetched from a backend server by enabling `ajax` option. 36 | 37 | There are three options given for hotspot-interactivity - hover, click and none. 38 | - **hover** : The data associated with the hotspot will show up on *hovering* over spot. 39 | - **click** : The data associated will show up on *clicking* the spot. 40 | - **none** : The data will be static and would not be affected by any events. 41 | 42 | ### Basic call with known coordinates: 43 | ``` 44 | $('#theElement').hotspot({ 45 | data: [ 46 | { "x":18, "y":38, "Title":"The Title","Message":"Create the Message here" }, 47 | { "x":43, "y":40, "Title":"jQuery Hotspot","Message":"This jQuery Plugin lets you create hotspot to any HTML element." } 48 | ], 49 | tag: 'img', //optional (default is img) 50 | interactivity: "hover", // options : click, none (default is hover) 51 | hotspotClass: 'Hotspot' 52 | }); 53 | ``` 54 | The `data` object above can be populated with any number of properties and can be designed with appropriate CSS. The `tag` variable passed to the plugin determines the type of HTML tag for which the hotspot is obtained. The `hotspotClass` is class of the hotspots created. One can change the look of the hotspot by applying some CSS to this class. 55 | 56 | By default in display mode, repositioning of hotspots on window resize is enabled. To disable this, a parameter can be passed in the plugin options. 57 | ``` 58 | listenOnResize: false 59 | ``` 60 | 61 | ### Fetching data via AJAX call 62 | ``` 63 | $("#theElement").hotspot({ 64 | ajax: true, // Switching on the ajax 65 | ajaxOptions: { 66 | url: '/hotspots', 67 | type: 'GET', 68 | ... 69 | }, 70 | interactivity: "hover" // options : click, none (default is hover) 71 | }); 72 | ``` 73 | The data coming from the server-side is expected to be JSON encoded. The object `ajaxOptions` can be utilized to change the http headers, etc. 74 | 75 | 76 | Usage - Admin mode 77 | -------------------- 78 | ### Standard call 79 | ``` 80 | $("#theElement").hotspot({ 81 | mode: "admin", // Switching to admin mode 82 | ajax: true, // Turn on ajax 83 | ajaxOptions: { 84 | url: '/hotspots', 85 | type: 'POST', 86 | ... 87 | }, 88 | interactivity: "hover" // options : click, none (default is hover) 89 | }); 90 | ``` 91 | `ajax` and `ajaxOptions` are required here so that the hotspot that are being created in the admin mode get stored in the server. However if someone turn off the ajax, then the hotspots data gets stored in the browser's LocalStorage. 92 | 93 | Key Features 94 | ------------ 95 | 96 | 1. Any number of *messages/nodes* can be stuffed in a single hotspot. To do so the `schema` parameter is passed to the plugin. By default there are two nodes (Title and Message) 97 | 98 | ``` 99 | $("#theElement").hotspot({ 100 | mode: "admin", // Switching to admin mode 101 | ajax: true, // Turn on ajax 102 | ajaxOptions: { 103 | url: '/hotspots', 104 | type: 'POST', 105 | ... 106 | }, 107 | schema: [ 108 | { 109 | 'property': 'Time', 110 | 'default': '5:40am' 111 | }, 112 | { 113 | 'property': 'Date', 114 | 'default': '12/11/2018' 115 | }, 116 | { 117 | 'property': 'Place', 118 | 'default': 'Siberia' 119 | }, 120 | ] 121 | }); 122 | ``` 123 | The `default` property in the `schema` object above will be overwritten everytime a new hotspot is created. 124 | 125 | 2. The plugin also provides some basic functions that can be invoked after certain events in the process of creation of hotspots. 126 | 127 | ``` 128 | $("#theElement").hotspot({ 129 | mode: "admin", // Switching to admin mode 130 | ajax: true, // Turn on ajax 131 | ajaxOptions: { 132 | url: '/hotspots', 133 | type: 'POST', 134 | ... 135 | }, 136 | afterSave: function(err, data) { 137 | // hotspots `data` in json format can be stored 138 | // & passed in `display` mode for the image 139 | }, 140 | afterRemove: function(err, message) { 141 | // `message` from the plugin 142 | }, 143 | afterSend: function(error, message) { 144 | // check if any `error` has occurred 145 | } 146 | }); 147 | ``` 148 | These functions can be utilized to keep track of the flow of hotspot creation. 149 | 150 | 3. The plugin can be used with any number of HTML elements in a page. However to do so, the `LS_Variable` passed to the plugin should be named uniquely for every instance. This helps plugin to avoid conflicts and while using HTML5 LocalStorage. 151 | 152 | HTML : 153 | ``` 154 |
155 | 156 |
157 |
158 | 159 |
160 | ``` 161 | 162 | JavaScript : 163 | 164 | ``` 165 | $("#theElement-a").hotspot({ 166 | mode: "admin", 167 | ... 168 | LS_Variable: "HotspotPlugin-a" 169 | }); 170 | 171 | $("#theElement-b").hotspot({ 172 | mode: "admin", 173 | ... 174 | LS_Variable: "HotspotPlugin-b" 175 | }); 176 | ``` 177 | 178 | For better clarity the examples folder can be checked 179 | 180 | 181 | Fetch data via AJAX Request 182 | ------------ 183 | 184 | Include jquery and hotspot plugin in the html code. 185 | 186 | ``` 187 | 195 | ``` 196 | 197 | Also make sure `result` passed as `data` in the plugin should be an array of objects. 198 | ``` 199 | [ 200 | { "x":18, "y":29, "Title":"The Title","Message":"Create the Message here" }, 201 | { "x":14, "y":40, "Title":"jQuery Hotspot","Message":"This jQuery Plugin lets you create hotspot to any HTML element." } 202 | ] 203 | ``` 204 | -------------------------------------------------------------------------------- /jquery.hotspot.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery Hotspot : A jQuery Plugin to create hotspot for an HTML element 3 | * 4 | * Author: Aniruddha Nath 5 | * Version: 2.0.3 6 | * 7 | * Website: https://github.com/aniruddhanath/jquery-hotspot 8 | * 9 | * Description: A jquery plugin for creating and displaying Hotspots in an HTML element. 10 | * It operates in two mode, admin and display. The design of the hotspot created are fully customizable. 11 | * User can add their own CSS class to style the hotspots. 12 | * 13 | * License: http://www.opensource.org/licenses/mit-license.php 14 | */ 15 | 16 | ;(function($) { 17 | 18 | var defaults = { 19 | 20 | // Object to hold the hotspot data points 21 | data: [], 22 | 23 | // Element tag upon which hotspot is (to be) build 24 | tag: 'img', 25 | 26 | // Specify mode in which the plugin is to be used 27 | // `admin`: Allows to create hotspot from UI 28 | // `display`: Display hotspots from `data` object 29 | mode: 'display', 30 | 31 | // HTML5 LocalStorage variable where hotspot data points are (will be) stored 32 | LS_Variable: '__HotspotPlugin_LocalStorage', 33 | 34 | // CSS class for hotspot data points 35 | hotspotClass: 'HotspotPlugin_Hotspot', 36 | 37 | // CSS class which is added when hotspot is to hidden 38 | hiddenClass: 'HotspotPlugin_Hotspot_Hidden', 39 | 40 | // Event on which the hotspot data point will show up 41 | // allowed values: `click`, `hover`, `none` 42 | interactivity: 'hover', 43 | 44 | // Action button CSS classes used in `admin` mode 45 | save_Button_Class: 'HotspotPlugin_Save', 46 | remove_Button_Class: 'HotspotPlugin_Remove', 47 | send_Button_Class: 'HotspotPlugin_Send', 48 | 49 | // CSS class for hotspot data points that are yet to be saved 50 | unsavedHotspotClass: 'HotspotPlugin_Hotspot_Unsaved', 51 | 52 | // CSS class for overlay used in `admin` mode 53 | hotspotOverlayClass: 'HotspotPlugin_Overlay', 54 | 55 | // Enable `ajax` to read data directly from server 56 | ajax: false, 57 | ajaxOptions: { url: '' }, 58 | 59 | listenOnResize: true, 60 | 61 | // Hotspot schema 62 | schema: [ 63 | { 64 | 'property': 'Title', 65 | 'default': 'jQuery Hotspot' 66 | }, 67 | { 68 | 'property': 'Message', 69 | 'default': 'This jQuery Plugin lets you create hotspot to any HTML element. ' 70 | } 71 | ] 72 | }; 73 | 74 | // Constructor 75 | function Hotspot(element, options) { 76 | 77 | var widget = this; 78 | 79 | // Overwriting defaults with options 80 | this.config = $.extend(true, {}, defaults, options); 81 | 82 | this.element = element; 83 | 84 | // `tagElement`: element for which hotspots are being done 85 | this.tagElement = element.find(this.config.tag); 86 | 87 | // Register event listeners 88 | $.each(this.config, function(index, fn) { 89 | if (typeof fn === 'function') { 90 | widget.element.on(index + '.hotspot', function(event, err, data) { 91 | fn(err, data); 92 | }); 93 | } 94 | }); 95 | 96 | if (this.config.mode != 'admin' && this.config.listenOnResize) { 97 | $(window).on('resize', function () { 98 | $(element).find('.' + widget.config.hotspotClass).remove(); 99 | widget.init(); 100 | }); 101 | } 102 | 103 | if (this.config.tag !== 'img') { 104 | widget.init(); 105 | return; 106 | } 107 | 108 | if (this.tagElement.prop('complete')) { 109 | widget.init(); 110 | } else { 111 | this.tagElement.one('load', function(event) { 112 | widget.init(); 113 | }); 114 | } 115 | } 116 | 117 | Hotspot.prototype.init = function() { 118 | this.parseData(); 119 | 120 | // Fetch data for `display` mode with `ajax` enabled 121 | if (this.config.mode != 'admin' && this.config.ajax) { 122 | this.fetchData(); 123 | } 124 | 125 | // Nothing else to do here for `display` mode 126 | if (this.config.mode != 'admin') { 127 | return; 128 | } 129 | 130 | this.setupWorkspace(); 131 | }; 132 | 133 | Hotspot.prototype.createId = function() { 134 | var id = ""; 135 | var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 136 | 137 | for (var i = 0; i < 7; i++) { 138 | id += letters.charAt(Math.floor(Math.random() * letters.length)); 139 | } 140 | 141 | return id; 142 | }; 143 | 144 | Hotspot.prototype.setupWorkspace = function() { 145 | var widget = this; 146 | 147 | // `data` array: to contain hotspot objects 148 | var data = []; 149 | 150 | var tHeight = $(widget.tagElement[0]).height(), 151 | tWidth = $(widget.tagElement[0]).width(), 152 | tOffset = widget.tagElement.offset(), 153 | pHeight = $(widget.element[0]).height(), 154 | pWidth = $(widget.element[0]).width(), 155 | pOffset = widget.element.offset(); 156 | 157 | // Create overlay for the tagElement 158 | $('', { 159 | html: '

This is Admin-mode. Click this Pane to Store Messages

' 160 | }).css({ 161 | 'height': (tHeight/pHeight)*100 + '%', 162 | 'width': (tWidth/pWidth)*100 + '%', 163 | 'left': (tOffset.left - pOffset.left) + 'px', 164 | 'top': (tOffset.top - pOffset.top) + 'px' 165 | }).addClass(widget.config.hotspotOverlayClass).appendTo(widget.element); 166 | 167 | // Handle click on overlay mask 168 | this.element.delegate('span', 'click', function(event) { 169 | event.preventDefault(); 170 | event.stopPropagation(); 171 | 172 | // Get coordinates 173 | var offset = $(this).offset(), 174 | relativeX = (event.pageX - offset.left), 175 | relativeY = (event.pageY - offset.top); 176 | 177 | var height = $(widget.tagElement[0]).height(), 178 | width = $(widget.tagElement[0]).width(); 179 | 180 | var hotspot = { x: relativeX/width*100, y: relativeY/height*100 }; 181 | 182 | var schema = widget.config.schema; 183 | 184 | for (var i = 0; i < schema.length; i++) { 185 | var val = schema[i]; 186 | var fill = prompt('Please enter ' + val.property, val.default); 187 | if (fill === null) { 188 | return; 189 | } 190 | hotspot[val.property] = fill; 191 | } 192 | 193 | data.push(hotspot); 194 | 195 | // Temporarily display the spot 196 | widget.displaySpot(hotspot, true); 197 | }); 198 | 199 | // Register admin controls 200 | var button_id = this.createId(); 201 | 202 | $('