How to bind multiple labels using leaflet.label? - javascript
I am using Leaflet and it works well. I am using leaflet.label as well and that works nicely as well. The problem is that I would like to display two labels to the right of the marker. If I call bindLabel twice, then the second overrides the first. How could I make sure that I have two labels, both to the right of the marker and the second label is above the first one?
This is how I tried:
newMarker.bindLabel(result, { noHide: true }).bindLabel("secondlabel", { noHide: true });
Thanks
EDIT:
I have managed to display the texts using a single call to bindLabel, like this:
newMarker.bindLabel(result + "<br>secondLabel", { noHide: true });
but this seems to be a too hacky solution.
Here they say it is not possible, but that was written in 2014. It might be possible since then.
I have managed to solve it. I am lazy to push it to their repo right now, but I will probably do it in the future. The logic of the solution is as follows:
this.label -> this.labels
this.labels is an array of LeafletLabels
apply the change for methods containing this.label
move this._labelNoHide = options.noHide into the if to prevent bugs
The labels will act similarly for the subset of options which is handled singularly / marker. Sorry, folks, singularizing noHide or opacity to label level instead of marker level is beyond the scope of this question. I might resolve those later though.
The code is as follows:
/*
Leaflet.label, a plugin that adds labels to markers and vectors for Leaflet powered maps.
(c) 2012-2013, Jacob Toye, Smartrak
https://github.com/Leaflet/Leaflet.label
http://leafletjs.com
https://github.com/jacobtoye
*/
(function (factory, window) {
// define an AMD module that relies on 'leaflet'
if (typeof define === 'function' && define.amd) {
define(['leaflet'], factory);
// define a Common JS module that relies on 'leaflet'
} else if (typeof exports === 'object') {
module.exports = factory(require('leaflet'));
}
// attach your plugin to the global 'L' variable
if (typeof window !== 'undefined' && window.L) {
window.LeafletLabel = factory(L);
}
}(function (L) {
L.labelVersion = '0.2.4';
var LeafletLabel = L.Class.extend({
includes: L.Mixin.Events,
options: {
className: '',
clickable: false,
direction: 'right',
noHide: false,
offset: [12, -15], // 6 (width of the label triangle) + 6 (padding)
opacity: 1,
zoomAnimation: true
},
initialize: function (options, source) {
L.setOptions(this, options);
this._source = source;
this._animated = L.Browser.any3d && this.options.zoomAnimation;
this._isOpen = false;
},
onAdd: function (map) {
this._map = map;
this._pane = this.options.pane ? map._panes[this.options.pane] :
this._source instanceof L.Marker ? map._panes.markerPane : map._panes.popupPane;
if (!this._container) {
this._initLayout();
}
this._pane.appendChild(this._container);
this._initInteraction();
this._update();
this.setOpacity(this.options.opacity);
map
.on('moveend', this._onMoveEnd, this)
.on('viewreset', this._onViewReset, this);
if (this._animated) {
map.on('zoomanim', this._zoomAnimation, this);
}
if (L.Browser.touch && !this.options.noHide) {
L.DomEvent.on(this._container, 'click', this.close, this);
map.on('click', this.close, this);
}
},
onRemove: function (map) {
this._pane.removeChild(this._container);
map.off({
zoomanim: this._zoomAnimation,
moveend: this._onMoveEnd,
viewreset: this._onViewReset
}, this);
this._removeInteraction();
this._map = null;
},
setLatLng: function (latlng) {
this._latlng = L.latLng(latlng);
if (this._map) {
this._updatePosition();
}
return this;
},
setContent: function (content) {
// Backup previous content and store new content
this._previousContent = this._content;
this._content = content;
this._updateContent();
return this;
},
close: function () {
var map = this._map;
if (map) {
if (L.Browser.touch && !this.options.noHide) {
L.DomEvent.off(this._container, 'click', this.close);
map.off('click', this.close, this);
}
map.removeLayer(this);
}
},
updateZIndex: function (zIndex) {
this._zIndex = zIndex;
if (this._container && this._zIndex) {
this._container.style.zIndex = zIndex;
}
},
setOpacity: function (opacity) {
this.options.opacity = opacity;
if (this._container) {
L.DomUtil.setOpacity(this._container, opacity);
}
},
_initLayout: function () {
this._container = L.DomUtil.create('div', 'leaflet-label ' + this.options.className + ' leaflet-zoom-animated');
this.updateZIndex(this._zIndex);
},
_update: function () {
if (!this._map) { return; }
this._container.style.visibility = 'hidden';
this._updateContent();
this._updatePosition();
this._container.style.visibility = '';
},
_updateContent: function () {
if (!this._content || !this._map || this._prevContent === this._content) {
return;
}
if (typeof this._content === 'string') {
this._container.innerHTML = this._content;
this._prevContent = this._content;
this._labelWidth = this._container.offsetWidth;
}
},
_updatePosition: function () {
var pos = this._map.latLngToLayerPoint(this._latlng);
this._setPosition(pos);
},
_setPosition: function (pos) {
var map = this._map,
container = this._container,
centerPoint = map.latLngToContainerPoint(map.getCenter()),
labelPoint = map.layerPointToContainerPoint(pos),
direction = this.options.direction,
labelWidth = this._labelWidth,
offset = L.point(this.options.offset);
// position to the right (right or auto & needs to)
if (direction === 'right' || direction === 'auto' && labelPoint.x < centerPoint.x) {
L.DomUtil.addClass(container, 'leaflet-label-right');
L.DomUtil.removeClass(container, 'leaflet-label-left');
pos = pos.add(offset);
} else { // position to the left
L.DomUtil.addClass(container, 'leaflet-label-left');
L.DomUtil.removeClass(container, 'leaflet-label-right');
pos = pos.add(L.point(-offset.x - labelWidth, offset.y));
}
L.DomUtil.setPosition(container, pos);
},
_zoomAnimation: function (opt) {
var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
this._setPosition(pos);
},
_onMoveEnd: function () {
if (!this._animated || this.options.direction === 'auto') {
this._updatePosition();
}
},
_onViewReset: function (e) {
/* if map resets hard, we must update the label */
if (e && e.hard) {
this._update();
}
},
_initInteraction: function () {
if (!this.options.clickable) { return; }
var container = this._container,
events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];
L.DomUtil.addClass(container, 'leaflet-clickable');
L.DomEvent.on(container, 'click', this._onMouseClick, this);
for (var i = 0; i < events.length; i++) {
L.DomEvent.on(container, events[i], this._fireMouseEvent, this);
}
},
_removeInteraction: function () {
if (!this.options.clickable) { return; }
var container = this._container,
events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];
L.DomUtil.removeClass(container, 'leaflet-clickable');
L.DomEvent.off(container, 'click', this._onMouseClick, this);
for (var i = 0; i < events.length; i++) {
L.DomEvent.off(container, events[i], this._fireMouseEvent, this);
}
},
_onMouseClick: function (e) {
if (this.hasEventListeners(e.type)) {
L.DomEvent.stopPropagation(e);
}
this.fire(e.type, {
originalEvent: e
});
},
_fireMouseEvent: function (e) {
this.fire(e.type, {
originalEvent: e
});
// TODO proper custom event propagation
// this line will always be called if marker is in a FeatureGroup
if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) {
L.DomEvent.preventDefault(e);
}
if (e.type !== 'mousedown') {
L.DomEvent.stopPropagation(e);
} else {
L.DomEvent.preventDefault(e);
}
}
});
/*global LeafletLabel */
// This object is a mixin for L.Marker and L.CircleMarker. We declare it here as both need to include the contents.
L.BaseMarkerMethods = {
showLabel: function () {
if (this.labels && this._map) {
for (var labelIndex in this.labels) {
this.labels[labelIndex].setLatLng(this._latlng);
this._map.showLabel(this.labels[labelIndex]);
}
}
return this;
},
hideLabel: function () {
if (this.labels) {
for (var labelIndex in this.labels) {
this.labels[labelIndex].close();
}
}
return this;
},
setLabelNoHide: function (noHide) {
if (this._labelNoHide === noHide) {
return;
}
this._labelNoHide = noHide;
if (noHide) {
this._removeLabelRevealHandlers();
this.showLabel();
} else {
this._addLabelRevealHandlers();
this.hideLabel();
}
},
bindLabel: function (content, options) {
var labelAnchor = this.options.icon ? this.options.icon.options.labelAnchor : this.options.labelAnchor,
anchor = L.point(labelAnchor) || L.point(0, 0);
anchor = anchor.add(LeafletLabel.prototype.options.offset);
if (options && options.offset) {
anchor = anchor.add(options.offset);
}
options = L.Util.extend({ offset: anchor }, options);
if (!this.labels) {
this._labelNoHide = options.noHide;
this.labels = [];
if (!this._labelNoHide) {
this._addLabelRevealHandlers();
}
this
.on('remove', this.hideLabel, this)
.on('move', this._moveLabel, this)
.on('add', this._onMarkerAdd, this);
this._hasLabelHandlers = true;
}
this.labels.push(new LeafletLabel(options, this).setContent(content));
return this;
},
unbindLabel: function () {
if (this.labels) {
this.hideLabel();
this.labels = null;
if (this._hasLabelHandlers) {
if (!this._labelNoHide) {
this._removeLabelRevealHandlers();
}
this
.off('remove', this.hideLabel, this)
.off('move', this._moveLabel, this)
.off('add', this._onMarkerAdd, this);
}
this._hasLabelHandlers = false;
}
return this;
},
updateLabelContent: function (content, index) {
if ((this.labels) && (index < this.labels.length)) {
this.labels[index].setContent(content);
}
},
getLabels: function () {
return this.labels;
},
_onMarkerAdd: function () {
if (this._labelNoHide) {
this.showLabel();
}
},
_addLabelRevealHandlers: function () {
this
.on('mouseover', this.showLabel, this)
.on('mouseout', this.hideLabel, this);
if (L.Browser.touch) {
this.on('click', this.showLabel, this);
}
},
_removeLabelRevealHandlers: function () {
this
.off('mouseover', this.showLabel, this)
.off('mouseout', this.hideLabel, this);
if (L.Browser.touch) {
this.off('click', this.showLabel, this);
}
},
_moveLabel: function (e) {
this.label.setLatLng(e.latlng);
}
};
// Add in an option to icon that is used to set where the label anchor is
L.Icon.Default.mergeOptions({
labelAnchor: new L.Point(9, -20)
});
// Have to do this since Leaflet is loaded before this plugin and initializes
// L.Marker.options.icon therefore missing our mixin above.
L.Marker.mergeOptions({
icon: new L.Icon.Default()
});
L.Marker.include(L.BaseMarkerMethods);
L.Marker.include({
_originalUpdateZIndex: L.Marker.prototype._updateZIndex,
_updateZIndex: function (offset) {
var zIndex = this._zIndex + offset;
this._originalUpdateZIndex(offset);
if (this.labels) {
for (var labelIndex in this.labels) {
this.labels[labelIndex].updateZIndex(zIndex);
}
}
},
_originalSetOpacity: L.Marker.prototype.setOpacity,
setOpacity: function (opacity, labelHasSemiTransparency) {
this.options.labelHasSemiTransparency = labelHasSemiTransparency;
this._originalSetOpacity(opacity);
},
_originalUpdateOpacity: L.Marker.prototype._updateOpacity,
_updateOpacity: function () {
var absoluteOpacity = this.options.opacity === 0 ? 0 : 1;
this._originalUpdateOpacity();
if (this.labels) {
for (var labelIndex in labels) {
this.labels[labelIndex].setOpacity(this.options.labelHasSemiTransparency ? this.options.opacity : absoluteOpacity);
}
}
},
_originalSetLatLng: L.Marker.prototype.setLatLng,
setLatLng: function (latlng) {
if (this.labels && !this._labelNoHide) {
this.hideLabel();
}
return this._originalSetLatLng(latlng);
}
});
// Add in an option to icon that is used to set where the label anchor is
L.CircleMarker.mergeOptions({
labelAnchor: new L.Point(0, 0)
});
L.CircleMarker.include(L.BaseMarkerMethods);
/*global LeafletLabel */
L.Path.include({
bindLabel: function (content, options) {
if (!this.label || this.label.options !== options) {
this.label = new LeafletLabel(options, this);
}
this.label.setContent(content);
if (!this._showLabelAdded) {
this
.on('mouseover', this._showLabel, this)
.on('mousemove', this._moveLabel, this)
.on('mouseout remove', this._hideLabel, this);
if (L.Browser.touch) {
this.on('click', this._showLabel, this);
}
this._showLabelAdded = true;
}
return this;
},
unbindLabel: function () {
if (this.label) {
this._hideLabel();
this.label = null;
this._showLabelAdded = false;
this
.off('mouseover', this._showLabel, this)
.off('mousemove', this._moveLabel, this)
.off('mouseout remove', this._hideLabel, this);
}
return this;
},
updateLabelContent: function (content) {
if (this.label) {
this.label.setContent(content);
}
},
_showLabel: function (e) {
this.label.setLatLng(e.latlng);
this._map.showLabel(this.label);
},
_moveLabel: function (e) {
this.label.setLatLng(e.latlng);
},
_hideLabel: function () {
this.label.close();
}
});
L.Map.include({
showLabel: function (label) {
return this.addLayer(label);
}
});
L.FeatureGroup.include({
// TODO: remove this when AOP is supported in Leaflet, need this as we cannot put code in removeLayer()
clearLayers: function () {
this.unbindLabel();
this.eachLayer(this.removeLayer, this);
return this;
},
bindLabel: function (content, options) {
return this.invoke('bindLabel', content, options);
},
unbindLabel: function () {
return this.invoke('unbindLabel');
},
updateLabelContent: function (content) {
this.invoke('updateLabelContent', content);
}
});
return LeafletLabel;
}, window));
Related
Bootstrap Modal Doesn't dismiss due to code conflict
I am using ELA bootstrap admin template. There is a sidebarmenu.js file included in the theme which is used for sidebar menu List Open/Close. Every thing works file. But I wanted to implement Bootstrap Modal, inside my application. On button click, modal pops up but 'x' or close button seems to be not working. Nether it closes on clicking outside the modal area. When I comment out inclusion of sidebarmenu.js from the header, the modal works fine. Here is sidebarmenu.js code: (function (global, factory) { if (typeof define === "function" && define.amd) { define(['jquery'], factory); } else if (typeof exports !== "undefined") { factory(require('jquery')); } else { var mod = { exports: {} }; factory(global.jquery); global.metisMenu = mod.exports; } })(this, function (_jquery) { 'use strict'; var _jquery2 = _interopRequireDefault(_jquery); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Util = function ($) { var transition = false; var TransitionEndEvent = { WebkitTransition: 'webkitTransitionEnd', MozTransition: 'transitionend', OTransition: 'oTransitionEnd otransitionend', transition: 'transitionend' }; function getSpecialTransitionEndEvent() { return { bindType: transition.end, delegateType: transition.end, handle: function handle(event) { if ($(event.target).is(this)) { return event.handleObj.handler.apply(this, arguments); } return undefined; } }; } function transitionEndTest() { if (window.QUnit) { return false; } var el = document.createElement('mm'); for (var name in TransitionEndEvent) { if (el.style[name] !== undefined) { return { end: TransitionEndEvent[name] }; } } return false; } function transitionEndEmulator(duration) { var _this2 = this; var called = false; $(this).one(Util.TRANSITION_END, function () { called = true; }); setTimeout(function () { if (!called) { Util.triggerTransitionEnd(_this2); } }, duration); return this; } function setTransitionEndSupport() { transition = transitionEndTest(); $.fn.emulateTransitionEnd = transitionEndEmulator; if (Util.supportsTransitionEnd()) { $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent(); } } var Util = { TRANSITION_END: 'mmTransitionEnd', triggerTransitionEnd: function triggerTransitionEnd(element) { $(element).trigger(transition.end); }, supportsTransitionEnd: function supportsTransitionEnd() { return Boolean(transition); } }; setTransitionEndSupport(); return Util; }(jQuery); var MetisMenu = function ($) { var NAME = 'metisMenu'; var DATA_KEY = 'metisMenu'; var EVENT_KEY = '.' + DATA_KEY; var DATA_API_KEY = '.data-api'; var JQUERY_NO_CONFLICT = $.fn[NAME]; var TRANSITION_DURATION = 350; var Default = { toggle: true, preventDefault: true, activeClass: 'active', collapseClass: 'collapse', collapseInClass: 'in', collapsingClass: 'collapsing', triggerElement: 'a', parentTrigger: 'li', subMenu: 'ul' }; var Event = { SHOW: 'show' + EVENT_KEY, SHOWN: 'shown' + EVENT_KEY, HIDE: 'hide' + EVENT_KEY, HIDDEN: 'hidden' + EVENT_KEY, CLICK_DATA_API: 'click' + EVENT_KEY + DATA_API_KEY }; var MetisMenu = function () { function MetisMenu(element, config) { _classCallCheck(this, MetisMenu); this._element = element; this._config = this._getConfig(config); this._transitioning = null; this.init(); } MetisMenu.prototype.init = function init() { var self = this; $(this._element).find(this._config.parentTrigger + '.' + this._config.activeClass).has(this._config.subMenu).children(this._config.subMenu).attr('aria-expanded', true).addClass(this._config.collapseClass + ' ' + this._config.collapseInClass); $(this._element).find(this._config.parentTrigger).not('.' + this._config.activeClass).has(this._config.subMenu).children(this._config.subMenu).attr('aria-expanded', false).addClass(this._config.collapseClass); $(this._element).find(this._config.parentTrigger).has(this._config.subMenu).children(this._config.triggerElement).on(Event.CLICK_DATA_API, function (e) { var _this = $(this); var _parent = _this.parent(self._config.parentTrigger); var _siblings = _parent.siblings(self._config.parentTrigger).children(self._config.triggerElement); var _list = _parent.children(self._config.subMenu); if (self._config.preventDefault) { e.preventDefault(); } if (_this.attr('aria-disabled') === 'true') { return; } if (_parent.hasClass(self._config.activeClass)) { _this.attr('aria-expanded', false); self._hide(_list); } else { self._show(_list); _this.attr('aria-expanded', true); if (self._config.toggle) { _siblings.attr('aria-expanded', false); } } if (self._config.onTransitionStart) { self._config.onTransitionStart(e); } }); }; MetisMenu.prototype._show = function _show(element) { if (this._transitioning || $(element).hasClass(this._config.collapsingClass)) { return; } var _this = this; var _el = $(element); var startEvent = $.Event(Event.SHOW); _el.trigger(startEvent); if (startEvent.isDefaultPrevented()) { return; } _el.parent(this._config.parentTrigger).addClass(this._config.activeClass); if (this._config.toggle) { this._hide(_el.parent(this._config.parentTrigger).siblings().children(this._config.subMenu + '.' + this._config.collapseInClass).attr('aria-expanded', false)); } _el.removeClass(this._config.collapseClass).addClass(this._config.collapsingClass).height(0); this.setTransitioning(true); var complete = function complete() { _el.removeClass(_this._config.collapsingClass).addClass(_this._config.collapseClass + ' ' + _this._config.collapseInClass).height('').attr('aria-expanded', true); _this.setTransitioning(false); _el.trigger(Event.SHOWN); }; if (!Util.supportsTransitionEnd()) { complete(); return; } _el.height(_el[0].scrollHeight).one(Util.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION); }; MetisMenu.prototype._hide = function _hide(element) { if (this._transitioning || !$(element).hasClass(this._config.collapseInClass)) { return; } var _this = this; var _el = $(element); var startEvent = $.Event(Event.HIDE); _el.trigger(startEvent); if (startEvent.isDefaultPrevented()) { return; } _el.parent(this._config.parentTrigger).removeClass(this._config.activeClass); _el.height(_el.height())[0].offsetHeight; _el.addClass(this._config.collapsingClass).removeClass(this._config.collapseClass).removeClass(this._config.collapseInClass); this.setTransitioning(true); var complete = function complete() { if (_this._transitioning && _this._config.onTransitionEnd) { _this._config.onTransitionEnd(); } _this.setTransitioning(false); _el.trigger(Event.HIDDEN); _el.removeClass(_this._config.collapsingClass).addClass(_this._config.collapseClass).attr('aria-expanded', false); }; if (!Util.supportsTransitionEnd()) { complete(); return; } _el.height() == 0 || _el.css('display') == 'none' ? complete() : _el.height(0).one(Util.TRANSITION_END, complete).emulateTransitionEnd(TRANSITION_DURATION); }; MetisMenu.prototype.setTransitioning = function setTransitioning(isTransitioning) { this._transitioning = isTransitioning; }; MetisMenu.prototype.dispose = function dispose() { $.removeData(this._element, DATA_KEY); $(this._element).find(this._config.parentTrigger).has(this._config.subMenu).children(this._config.triggerElement).off('click'); this._transitioning = null; this._config = null; this._element = null; }; MetisMenu.prototype._getConfig = function _getConfig(config) { config = $.extend({}, Default, config); return config; }; MetisMenu._jQueryInterface = function _jQueryInterface(config) { return this.each(function () { var $this = $(this); var data = $this.data(DATA_KEY); var _config = $.extend({}, Default, $this.data(), (typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object' && config); if (!data && /dispose/.test(config)) { this.dispose(); } if (!data) { data = new MetisMenu(this, _config); $this.data(DATA_KEY, data); } if (typeof config === 'string') { if (data[config] === undefined) { throw new Error('No method named "' + config + '"'); } data[config](); } }); }; return MetisMenu; }(); /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ */ $.fn[NAME] = MetisMenu._jQueryInterface; $.fn[NAME].Constructor = MetisMenu; $.fn[NAME].noConflict = function () { $.fn[NAME] = JQUERY_NO_CONFLICT; return MetisMenu._jQueryInterface; }; return MetisMenu; }(jQuery); }); I don't know why this code is having conflict with the dismissal of bootstrap modal dialog. Since, I am beginner in JavaScript/JQuery I am unable to find which section of above code is causing problem. Thanks in advance.
Tab Navigation not working for Autocomplete text box on Mozilla Firefox
I am using 'jquery.ui.autocomplete.js'. What issue I am facing is that during TAB Press default Naviagtion get stuck when it reached in Autocomplete Text Box. The issue is reflecting only in Mozilla Firefox. What I want is that on TAB key press it shold move to next elemnt. Can anyone please assist me to fix this issue? I have tried Googled solutions but not able to fix. I am posting some link hope it will help you to understand isue. 1. http://bugs.jqueryui.com/ticket/6661 I think issue is somewhere in these lines: case keyCode.ENTER: case keyCode.NUMPAD_ENTER: // when menu is open or has focus if (self.menu.element.is(":visible")) { event.preventDefault(); } //passthrough - ENTER and TAB both select the current element case keyCode.TAB: if (!self.menu.active) { return; } self.menu.select(event); break; case keyCode.ESCAPE: self.element.val(self.term); self.close(event); break; default: // keypress is triggered before the input value is changed clearTimeout(self.searching); self.searching = setTimeout(function () { // only search if the value has changed if (self.term != self.element.val()) { self.selectedItem = null; self.search(null, event); } }, self.options.delay); break; } }) My jquery code is here: (function ($, undefined) { $.widget("ui.autocomplete", { options: { appendTo: "body", delay: 300, minLength: 1, position: { my: "left top", at: "left bottom", collision: "none" }, source: null }, _create: function () { var self = this, doc = this.element[0].ownerDocument; this.element .addClass("ui-autocomplete-input") .attr("autocomplete", "off") // TODO verify these actually work as intended .attr({ role: "textbox", "aria-autocomplete": "list", "aria-haspopup": "true" }) .bind("keydown.autocomplete", function (event) { if (self.options.disabled || self.element.attr("readonly")) { return; } var keyCode = $.ui.keyCode; switch (event.keyCode) { case keyCode.PAGE_UP: self._move("previousPage", event); break; case keyCode.PAGE_DOWN: self._move("nextPage", event); break; case keyCode.UP: self._move("previous", event); // prevent moving cursor to beginning of text field in some browsers event.preventDefault(); break; case keyCode.DOWN: self._move("next", event); // prevent moving cursor to end of text field in some browsers event.preventDefault(); break; case keyCode.ENTER: case keyCode.NUMPAD_ENTER: // when menu is open or has focus if (self.menu.element.is(":visible")) { event.preventDefault(); } //passthrough - ENTER and TAB both select the current element case keyCode.TAB: if (!self.menu.active) { return; } self.menu.select(event); break; case keyCode.ESCAPE: self.element.val(self.term); self.close(event); break; default: // keypress is triggered before the input value is changed clearTimeout(self.searching); self.searching = setTimeout(function () { // only search if the value has changed if (self.term != self.element.val()) { self.selectedItem = null; self.search(null, event); } }, self.options.delay); break; } }) .bind("focus.autocomplete", function () { if (self.options.disabled) { return; } self.selectedItem = null; self.previous = self.element.val(); }) .bind("blur.autocomplete", function (event) { if (self.options.disabled) { return; } clearTimeout(self.searching); // clicks on the menu (or a button to trigger a search) will cause a blur event self.closing = setTimeout(function () { self.close(event); self._change(event); }, 150); }); this._initSource(); this.response = function () { return self._response.apply(self, arguments); }; this.menu = $("<ul></ul>") .addClass("ui-autocomplete") .appendTo($(this.options.appendTo || "body", doc)[0]) // prevent the close-on-blur in case of a "slow" click on the menu (long mousedown) .mousedown(function (event) { // clicking on the scrollbar causes focus to shift to the body // but we can't detect a mouseup or a click immediately afterward // so we have to track the next mousedown and close the menu if // the user clicks somewhere outside of the autocomplete var menuElement = self.menu.element[0]; if (event.target === menuElement) { setTimeout(function () { $(document).one('mousedown', function (event) { if (event.target !== self.element[0] && event.target !== menuElement && !$.ui.contains(menuElement, event.target)) { self.close(); } }); }, 1); } // use another timeout to make sure the blur-event-handler on the input was already triggered setTimeout(function () { clearTimeout(self.closing); }, 13); }) .menu({ focus: function (event, ui) { var item = ui.item.data("item.autocomplete"); if (false !== self._trigger("focus", null, { item: item })) { // use value to match what will end up in the input, if it was a key event if (/^key/.test(event.originalEvent.type)) { self.element.val(item.value); } } }, selected: function (event, ui) { var item = ui.item.data("item.autocomplete"), previous = self.previous; // only trigger when focus was lost (click on menu) if (self.element[0] !== doc.activeElement) { self.element.focus(); self.previous = previous; } if (false !== self._trigger("select", event, { item: item })) { self.element.val(item.value); } self.close(event); self.selectedItem = item; }, blur: function (event, ui) { // don't set the value of the text field if it's already correct // this prevents moving the cursor unnecessarily if (self.menu.element.is(":visible") && (self.element.val() !== self.term)) { self.element.val(self.term); } } }) .zIndex(this.element.zIndex() + 1) // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781 .css({ top: 0, left: 0 }) .hide() .data("menu"); if ($.fn.bgiframe) { this.menu.element.bgiframe(); } }, destroy: function () { this.element .removeClass("ui-autocomplete-input") .removeAttr("autocomplete") .removeAttr("role") .removeAttr("aria-autocomplete") .removeAttr("aria-haspopup"); this.menu.element.remove(); $.Widget.prototype.destroy.call(this); }, _setOption: function (key, value) { $.Widget.prototype._setOption.apply(this, arguments); if (key === "source") { this._initSource(); } if (key === "appendTo") { this.menu.element.appendTo($(value || "body", this.element[0].ownerDocument)[0]) } }, _initSource: function () { var array, url; if ($.isArray(this.options.source)) { array = this.options.source; this.source = function (request, response) { response($.ui.autocomplete.filter(array, request.term)); }; } else if (typeof this.options.source === "string") { url = this.options.source; this.source = function (request, response) { $.getJSON(url, request, response); }; } else { this.source = this.options.source; } }, search: function (value, event) { value = value != null ? value : this.element.val(); if (value.length < this.options.minLength) { return this.close(event); } clearTimeout(this.closing); if (this._trigger("search") === false) { return; } return this._search(value); }, _search: function (value) { this.term = this.element .addClass("ui-autocomplete-loading") // always save the actual value, not the one passed as an argument .val(); this.source({ term: value }, this.response); }, _response: function (content) { if (content.length) { content = this._normalize(content); this._suggest(content); this._trigger("open"); } else { this.close(); } this.element.removeClass("ui-autocomplete-loading"); }, close: function (event) { clearTimeout(this.closing); if (this.menu.element.is(":visible")) { this._trigger("close", event); this.menu.element.hide(); this.menu.deactivate(); } }, _change: function (event) { if (this.previous !== this.element.val()) { this._trigger("change", event, { item: this.selectedItem }); } }, _normalize: function (items) { // assume all items have the right format when the first item is complete if (items.length && items[0].label && items[0].value) { return items; } return $.map(items, function (item) { if (typeof item === "string") { return { label: item, value: item }; } return $.extend({ label: item.label || item.value, value: item.value || item.label }, item); }); }, _suggest: function (items) { var ul = this.menu.element .empty() .zIndex(this.element.zIndex() + 1), menuWidth, textWidth; this._renderMenu(ul, items); // TODO refresh should check if the active item is still in the dom, removing the need for a manual deactivate this.menu.deactivate(); this.menu.refresh(); this.menu.element.show().position($.extend({ of: this.element }, this.options.position)); menuWidth = ul.width("").outerWidth(); textWidth = this.element.outerWidth(); ul.outerWidth(Math.max(menuWidth, textWidth)); }, _renderMenu: function (ul, items) { var self = this; $.each(items, function (index, item) { self._renderItem(ul, item); }); }, _renderItem: function (ul, item) { return $("<li></li>") .data("item.autocomplete", item) .append($("<a></a>").text(item.label)) .appendTo(ul); }, _move: function (direction, event) { if (!this.menu.element.is(":visible")) { this.search(null, event); return; } if (this.menu.first() && /^previous/.test(direction) || this.menu.last() && /^next/.test(direction)) { this.element.val(this.term); this.menu.deactivate(); return; } this.menu[direction](event); }, widget: function () { return this.menu.element; } }); $.extend($.ui.autocomplete, { escapeRegex: function (value) { return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); }, filter: function (array, term) { var matcher = new RegExp($.ui.autocomplete.escapeRegex(term), "i"); return $.grep(array, function (value) { return matcher.test(value.label || value.value || value); }); } }); } (jQuery));
Yes, that bug ticket you linked to shows the changeset you need to follow. Notice how instead of saying $('selector').autocomplete(...) you're expected to do: $('selector').bind('keydown', function (event) { if (event.keyCode === $.ui.keyCode.TAB && $(this).data('autocomplete').menu.active ) { event.preventDefault(); } }).autocomplete(...) Notice that you're intercepting TAB keydowns if the menu is "active" and preventing the event from doing the default behavior, which in a browser is focusing the next statically laid out element.
Jquery quicksearch issue with Sharepoint 2013
I'm using a jquery plugin called quicksearch within Sharepoint 2010 and it works perfectly. Unfortunately were being forced to migrate onto sharepoint 2013 and it's stopped working. An error is shown saying that the function is undefined. I believe I've narrowed this down to the quicksearch function itself. Here is the preliminary code: _spBodyOnLoadFunctionNames.push("Load"); $('[name=search]').on('keyup', function(){ Load(); }); function Load() { var searchArea = "#cbqwpctl00_ctl22_g_ca6bb172_1ab4_430d_ae38_a32cfa03b56b ul li"; var qs = $('input#id_search_list').val(); qs.quicksearch(searchArea); $('.filter input').on('change', function(){ checkAndHide() //$(searchArea).unhighlight(); }); function checkAndHide(){ var inputs = $('.filter input'); var i =0; for (var i = 0; i < inputs.length; i++){ if (!inputs[i].checked){ $('.' + inputs[i].name).addClass('filter-hide'); } else { $('.' + inputs[i].name).removeClass('filter-hide'); }; }; }; } Here is an example of the quicksearch library I'm using: (function($, window, document, undefined) { $.fn.quicksearch = function (target, opt) { var timeout, cache, rowcache, jq_results, val = '', e = this, options = $.extend({ delay: 300, selector: null, stripeRows: null, loader: null, noResults: 'div#noresults', bind: 'keyup keydown', onBefore: function () { var ar = $('input#id_search_list').val() if (ar.length > 2) { var i=0; var ar2 = $('input#id_search_list').val().split(" "); for (i = 0; i < ar2.length; i++) { $(searchArea + ':visible'); } return true; } return false; checkAndHide() }, onAfter: function () { return; }, show: function () { this.style.display = "block"; }, hide: function () { this.style.display = "none"; }, prepareQuery: function (val) { return val.toLowerCase().split(' '); }, testQuery: function (query, txt, _row) { for (var i = 0; i < query.length; i += 1) { if (txt.indexOf(query[i]) === -1) { return false; } } return true; } }, opt); this.go = function () { var i = 0, noresults = true, query = options.prepareQuery(val), val_empty = (val.replace(' ', '').length === 0); for (var i = 0, len = rowcache.length; i < len; i++) { if (val_empty) { options.hide.apply(rowcache[i]); noresults = false; } else if (options.testQuery(query, cache[i], rowcache[i])){ options.show.apply(rowcache[i]); noresults = false; } else { options.hide.apply(rowcache[i]); } } if (noresults) { this.results(false); } else { this.results(true); this.stripe(); } this.loader(false); options.onAfter(); return this; }; this.stripe = function () { if (typeof options.stripeRows === "object" && options.stripeRows !== null) { var joined = options.stripeRows.join(' '); var stripeRows_length = options.stripeRows.length; jq_results.not(':hidden').each(function (i) { $(this).removeClass(joined).addClass(options.stripeRows[i % stripeRows_length]); }); } return this; }; this.strip_html = function (input) { var output = input.replace(new RegExp('<[^<]+\>', 'g'), ""); output = $.trim(output.toLowerCase()); return output; }; this.results = function (bool) { if (typeof options.noResults === "string" && options.noResults !== "") { if (bool) { $(options.noResults).hide(); } else { $(options.noResults).show(); } } return this; }; this.loader = function (bool) { if (typeof options.loader === "string" && options.loader !== "") { (bool) ? $(options.loader).show() : $(options.loader).hide(); } return this; }; this.cache = function () { jq_results = $(target); if (typeof options.noResults === "string" && options.noResults !== "") { jq_results = jq_results.not(options.noResults); } var t = (typeof options.selector === "string") ? jq_results.find(options.selector) : $(target).not(options.noResults); cache = t.map(function () { return e.strip_html(this.innerHTML); }); rowcache = jq_results.map(function () { return this; }); return this.go(); }; this.trigger = function () { this.loader(true); if (options.onBefore()) { window.clearTimeout(timeout); timeout = window.setTimeout(function () { e.go(); }, options.delay); } return this; }; this.cache(); this.results(true); this.stripe(); this.loader(false); return this.each(function () { $(this).bind(options.bind, function () { val = $(this).val(); e.trigger(); }); }); }; }(jQuery, this, document)); ` This is where the error comes up: var qs = $('input#id_search_list').val(); qs.quicksearch(searchArea); Any help would be appreciated
Turns out was a small issue in the code and major css as sharepoint plays differently in 2013
How to call jQuery widget public method outside using plugin reference
I have created a plug-in popup and trying to create reference and access the public method of the plug-in but it is giving an error 'Undefined Function'. var pop = $('.divCls').popup(); pop.setTitle('Sample Text');//This is line giving error Here is my plug-in (function ($) { $.widget('bs.popup', { options:{ // containment: '.bs-editor-body', hideOnClick: '.bs-editor-body', showTimeDelay:0 }, _create: function (){ var me = this, el = me.element, opts = me.options; el.addClass('bs-popup'); el.html($('<div class="bs-popup-body">' + el.html() + '</div>')); el.body = el.find('.bs-popup-body'); me._refresh(); if(opts.draggable){ el.draggable({containment:'document',cancel:'.bs-popup-body'}); el.unbind().bind('click',function(event){ event.stopPropagation(); }); } $(document).on('click','body',function(){ me.hide(); }); me.show(); }, _init:function(){ this.show(); this._refresh(); }, _refresh:function(){ var me = this, el = me.element, opts = me.options; el.find('.bs-popup-header').remove(); if(opts.title && opts.title !='') { el.prepend('<div class="bs-popup-header"></div>'); el.hdr = el.find('.bs-popup-header'); me.setTitle(opts.title); el.body.css({'border-top-left-radius':0,'border-top-right-radius':0}) } else { el.body.css({'border-radius': el.body.css('border-bottom-left-radius')}) } if(opts.width)me.setWidth(opts.width); if(opts.height)me.setHeight(opts.height); if(opts.left && opts.top) me.setXY(opts.left ,opts.top); if(opts.cls) el.addClass(opts.cls); }, show: function () { debugger; this.hide(); this.element.show(this.options.showTimeDelay); }, hide: function () { this.element.hide(); this._destroy(); }, setTitle:function(title){ this.getHeader().text(title); }, setXY: function (x,y){ var me = this, el = me.getElement(), opts = me.options; if (x && y) { el.css({'left': x, top: y}); } else { // var confine = evtRef.closest(opts.containment); } }, setWidth: function (value){ this.getBody().css({width: value}); }, setHeight: function (value) { this.getBody().css({height: value}); }, getElement:function(){ return this.element; }, getHeader:function(){ return this.element.hdr; }, getBody:function(){ return this.element.body; }, _destroy:function(){ // $.Widget.prototype.destroy.call(this); } }); }(jQuery));
You can try pop.popup('setTitle', 'Sample Text'); Widget Method Invocation To invoke a method using the widget's plugin, pass the name of the method as a string. Widget Public Methods
jQuery each with SetTimeout
Can anyone tell me why this doesn't work? jeMarkers is an array of Google Maps markers. function toggleBounce() { var bcounter = 0; $(jeMarkers).each(function () { setTimeout(function () { if (this.getAnimation() != null) { this.setAnimation(null); } else { this.setAnimation(google.maps.Animation.BOUNCE); } }, bcounter * 100); bcounter++; }); } If I do the same without the setTimeout function it works but obviously does all the markers at once: function toggleBounce() { $.each(jeMarkers, function () { if (this.getAnimation() != null) { this.setAnimation(null); } else { this.setAnimation(google.maps.Animation.BOUNCE); } });
You have to cache the this object inside the function since the context of the setTimeout is not automatically set: function toggleBounce() { var bcounter = 0; $(jeMarkers).each(function () { var that = this; // <- Cache the item setTimeout(function () { if (that.getAnimation() != null) { that.setAnimation(null); // <- Now we can call stuff on the item } else { that.setAnimation(google.maps.Animation.BOUNCE); } }, bcounter * 100); bcounter++; }); }