How to close my drop down menu in mobile view - javascript
My drop down menu opens in mobile view, but when clicking to close, nothing happens.
I've looked at the JS and examples online on what could be wrong but still cant figure it out. Also not sure which js query needs to be udpated as there are 3 different ones; accordion, collapse and 1.112.min.
Screenshot of dropdown menu error
Code:
<!-- navbar menu -->
<div class="collapse navbar-collapse" id="navbar-menu">
<ul class="nav navbar-nav navbar-right">
<li>Our Story</li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false">Solutions</a>
<ul class="dropdown-menu" id="menu">
<li>Point of Sale (POS)</li>
<li>Business Intelligence & Reporting </li>
<li>Integrations</li>
</ul>
</li>
<li>Our Customers</li>
<li>What's New</li>
<li>Contact</li>
</ul>
</div><!-- /.navbar-collapse -->
jquery collapse
(function($) {
// Constructor
function Collapse (el, options) {
options = options || {};
var _this = this,
query = options.query || "> :even";
$.extend(_this, {
$el: el,
options : options,
sections: [],
isAccordion : options.accordion || false,
db : options.persist ? jQueryCollapseStorage(el.get(0).id) : false
});
// Figure out what sections are open if storage is used
_this.states = _this.db ? _this.db.read() : [];
// For every pair of elements in given
// element, create a section
_this.$el.find(query).each(function() {
new jQueryCollapseSection($(this), _this);
});
// Capute ALL the clicks!
(function(scope) {
_this.$el.on("click", "[data-collapse-summary] " + (scope.options.clickQuery || ""),
$.proxy(_this.handleClick, scope));
_this.$el.bind("toggle close open",
$.proxy(_this.handleEvent, scope));
}(_this));
}
Collapse.prototype = {
handleClick: function(e, state) {
e.preventDefault();
var state = state || "toggle"
var sections = this.sections,
l = sections.length;
while(l--) {
if($.contains(sections[l].$summary[0], e.target)) {
sections[l][state]();
break;
}
}
},
handleEvent: function(e) {
if(e.target == this.$el.get(0)) return this[e.type]();
this.handleClick(e, e.type);
},
open: function(eq) {
if(isFinite(eq)) return this.sections[eq].open();
$.each(this.sections, function(i, section) {
section.open();
})
},
close: function(eq) {
if(isFinite(eq)) return this.sections[eq].close();
$.each(this.sections, function(i, section) {
section.close();
})
},
toggle: function(eq) {
if(isFinite(eq)) return this.sections[eq].toggle();
$.each(this.sections, function(i, section) {
section.toggle();
})
}
};
// Section constructor
function Section($el, parent) {
if(!parent.options.clickQuery) $el.wrapInner('<a href="#"/>');
$.extend(this, {
isOpen : false,
$summary : $el.attr("data-collapse-summary",""),
$details : $el.next(),
options: parent.options,
parent: parent
});
parent.sections.push(this);
// Check current state of section
var state = parent.states[this._index()];
if(state === 0) {
this.close(true)
}
else if(this.$summary.is(".open") || state === 1) {
this.open(true);
} else {
this.close(true)
}
}
Section.prototype = {
toggle : function() {
this.isOpen ? this.close() : this.open();
},
close: function(bypass) {
this._changeState("close", bypass);
},
open: function(bypass) {
var _this = this;
if(_this.options.accordion && !bypass) {
$.each(_this.parent.sections, function(i, section) {
section.close()
});
}
_this._changeState("open", bypass);
},
_index: function() {
return $.inArray(this, this.parent.sections);
},
_changeState: function(state, bypass) {
var _this = this;
_this.isOpen = state == "open";
if($.isFunction(_this.options[state]) && !bypass) {
_this.options[state].apply(_this.$details);
} else {
_this.$details[_this.isOpen ? "show" : "hide"]();
}
_this.$summary.toggleClass("open", state != "close")
_this.$details.attr("aria-hidden", state == "close");
_this.$summary.attr("aria-expanded", state == "open");
_this.$summary.trigger(state == "open" ? "opened" : "closed", _this);
if(_this.parent.db) {
_this.parent.db.write(_this._index(), _this.isOpen);
}
}
};
// Expose in jQuery API
$.fn.extend({
collapse: function(options, scan) {
var nodes = (scan) ? $("body").find("[data-collapse]") : $(this);
return nodes.each(function() {
var settings = (scan) ? {} : options,
values = $(this).attr("data-collapse") || "";
$.each(values.split(" "), function(i,v) {
if(v) settings[v] = true;
});
new Collapse($(this), settings);
});
}
});
//jQuery DOM Ready
$(function() {
$.fn.collapse(false, true);
});
// Expose constructor to
// global namespace
jQueryCollapse = Collapse;
jQueryCollapseSection = Section;
})(window.jQuery);
jquery accordion
(function ($) {
$.fn.accordion = function (options) {
//firewalling
if (!this || this.length < 1) {
return this;
}
initialize(this, options);
};
//create the initial accordion
function initialize(obj, options) {
//build main options before element iteration
var opts = $.extend({}, $.fn.accordion.defaults, options);
//store any opened default values to set cookie later
var opened = '';
//iterate each matched object, bind, and open/close
obj.each(function () {
var $this = $(this);
saveOpts($this, opts);
//bind it to the event
if (opts.bind == 'mouseenter') {
$this.bind('mouseenter', function (e) {
e.preventDefault();
toggle($this, opts);
});
}
//bind it to the event
if (opts.bind == 'mouseover') {
$this.bind('mouseover', function (e) {
e.preventDefault();
toggle($this, opts);
});
}
//bind it to the event
if (opts.bind == 'click') {
$this.bind('click', function (e) {
e.preventDefault();
toggle($this, opts);
});
}
//bind it to the event
if (opts.bind == 'dblclick') {
$this.bind('dblclick', function (e) {
e.preventDefault();
toggle($this, opts);
});
}
//initialize the panels
//get the id for this element
id = $this.attr('id');
//if not using cookies, open defaults
if (!useCookies(opts)) {
//close it if not defaulted to open
if (id != opts.defaultOpen) {
$this.addClass(opts.cssClose);
opts.loadClose($this, opts);
} else { //its a default open, open it
$this.addClass(opts.cssOpen);
opts.loadOpen($this, opts);
opened = id;
}
} else { //can use cookies, use them now
//has a cookie been set, this overrides default open
if (issetCookie(opts)) {
if (inCookie(id, opts) === false) {
$this.addClass(opts.cssClose);
opts.loadClose($this, opts);
} else {
$this.addClass(opts.cssOpen);
opts.loadOpen($this, opts);
opened = id;
}
} else { //a cookie hasn't been set open defaults
if (id != opts.defaultOpen) {
$this.addClass(opts.cssClose);
opts.loadClose($this, opts);
} else { //its a default open, open it
$this.addClass(opts.cssOpen);
opts.loadOpen($this, opts);
opened = id;
}
}
}
});
//now that the loop is done, set the cookie
if (opened.length > 0 && useCookies(opts)) {
setCookie(opened, opts);
} else { //there are none open, set cookie
setCookie('', opts);
}
return obj;
}
;
//load opts from object
function loadOpts($this) {
return $this.data('accordion-opts');
}
//save opts into object
function saveOpts($this, opts) {
return $this.data('accordion-opts', opts);
}
//hides a accordion panel
function close(opts) {
opened = $(document).find('.' + opts.cssOpen);
$.each(opened, function () {
//give the proper class to the linked element
$(this).addClass(opts.cssClose).removeClass(opts.cssOpen);
opts.animateClose($(this), opts);
});
}
//opens a accordion panel
function open($this, opts) {
close(opts);
//give the proper class to the linked element
$this.removeClass(opts.cssClose).addClass(opts.cssOpen);
//open the element
opts.animateOpen($this, opts);
//do cookies if plugin available
if (useCookies(opts)) {
// split the cookieOpen string by ","
id = $this.attr('id');
setCookie(id, opts);
}
}
//toggle a accordion on an event
function toggle($this, opts) {
// close the only open item
if ($this.hasClass(opts.cssOpen))
{
close(opts);
//do cookies if plugin available
if (useCookies(opts)) {
// split the cookieOpen string by ","
setCookie('', opts);
}
return false;
}
close(opts);
//open a closed element
open($this, opts);
return false;
}
//use cookies?
function useCookies(opts) {
//return false if cookie plugin not present or if a cookie name is not provided
if (!$.cookie || opts.cookieName == '') {
return false;
}
//we can use cookies
return true;
}
//set a cookie
function setCookie(value, opts)
{
//can use the cookie plugin
if (!useCookies(opts)) { //no, quit here
return false;
}
//cookie plugin is available, lets set the cookie
$.cookie(opts.cookieName, value, opts.cookieOptions);
}
//check if a accordion is in the cookie
function inCookie(value, opts)
{
//can use the cookie plugin
if (!useCookies(opts)) {
return false;
}
//if its not there we don't need to remove from it
if (!issetCookie(opts)) { //quit here, don't have a cookie
return false;
}
//unescape it
cookie = unescape($.cookie(opts.cookieName));
//is this value in the cookie arrray
if (cookie != value) { //no, quit here
return false;
}
return true;
}
//check if a cookie is set
function issetCookie(opts)
{
//can we use the cookie plugin
if (!useCookies(opts)) { //no, quit here
return false;
}
//is the cookie set
if ($.cookie(opts.cookieName) == null) { //no, quit here
return false;
}
return true;
}
// settings
$.fn.accordion.defaults = {
cssClose: 'accordion-close', //class you want to assign to a closed accordion header
cssOpen: 'accordion-open', //class you want to assign an opened accordion header
cookieName: 'accordion', //name of the cookie you want to set for this accordion
cookieOptions: {//cookie options, see cookie plugin for details
path: '/',
expires: 7,
domain: '',
secure: ''
},
defaultOpen: '', //id that you want opened by default
speed: 'slow', //speed of the slide effect
bind: 'click', //event to bind to, supports click, dblclick, mouseover and mouseenter
animateOpen: function (elem, opts) { //replace the standard slideDown with custom function
elem.next().stop(true, true).slideDown(opts.speed);
},
animateClose: function (elem, opts) { //replace the standard slideUp with custom function
elem.next().stop(true, true).slideUp(opts.speed);
},
loadOpen: function (elem, opts) { //replace the default open state with custom function
elem.next().show();
},
loadClose: function (elem, opts) { //replace the default close state with custom function
elem.next().hide();
}
};
})(jQuery);
Related
Hide mobile menu with swipe gesture instead of click
I'm trying to hide a hamburger menu with swipe left gesture. By default, we need to touch-click next to it in order to close it. I have loaded a very lightweight library in order for this touch action to work (http://www.netcu.de/jquery-touchwipe-iphone-ipad-library). // Clicking out of drawer closes it this.nodes.$page.on( 'click.drawer', $.proxy(function() { this.close(); return false; }, this) ); // Pressing escape closes drawer this.nodes.$parent.on( 'keyup.drawer', $.proxy(function(evt) { if (evt.keyCode === 27) { this.close(); } }, this) ); // This is my own code - swiping left closes it $( '#NavDrawer' ).touchwipe({ wipeLeft: function() { alert("yes, you swiped left on the menu"); // Now what ? }, }); You can have a live view here : https://themes.shopify.com/themes/venture/styles/outdoors/preview?mobile=true I first tried to use the actual structure to hide the menu, only with a swipe action, but it got all glitchy. Among other things, I don't understand where the 'keyup.drawer' and 'click.drawer' comes from. Ideally, I'd replicate this piece of code and use 'swipeleft.drawer' instead. Something like this : // Swiping left method this.nodes.$page.on( 'swipeleft.drawer', $.proxy(function() { this.close(); return false; }, this) ); Here's the full code of the global function if it might help : window.Drawers = (function() { var Drawer = function(id, position, options) { var defaults = { close: '.js-drawer-close', open: '.js-drawer-open-' + position, openClass: 'js-drawer-open', dirOpenClass: 'js-drawer-open-' + position }; this.nodes = { $parent: $('body, html'), $page: $('.page-element'), $moved: $('.is-moved-by-drawer') }; this.config = $.extend(defaults, options); this.position = position; this.$drawer = $('#' + id); this.$open = $(this.config.open); if (!this.$drawer.length) { return false; } this.drawerIsOpen = false; this.init(); }; Drawer.prototype.init = function() { this.$open.attr('aria-expanded', 'false'); this.$open.on('click', $.proxy(this.open, this)); this.$drawer.find(this.config.close).on('click', $.proxy(this.close, this)); }; Drawer.prototype.open = function(evt) { // Keep track if drawer was opened from a click, or called by another function var externalCall = false; // don't open an opened drawer if (this.drawerIsOpen) { return; } this.$open.addClass(this.config.openClass); // Prevent following href if link is clicked if (evt) { evt.preventDefault(); } else { externalCall = true; } // Without this, the drawer opens, the click event bubbles up to $nodes.page // which closes the drawer. if (evt && evt.stopPropagation) { evt.stopPropagation(); // save the source of the click, we'll focus to this on close this.$activeSource = $(evt.currentTarget); } if (this.drawerIsOpen && !externalCall) { return this.close(); } // Add is-transitioning class to moved elements on open so drawer can have // transition for close animation this.nodes.$moved.addClass('is-transitioning'); this.$drawer.prepareTransition(); this.nodes.$parent.addClass( this.config.openClass + ' ' + this.config.dirOpenClass ); this.drawerIsOpen = true; // Set focus on drawer slate.a11y.trapFocus({ $container: this.$drawer, namespace: 'drawer_focus' }); // Run function when draw opens if set if ( this.config.onDrawerOpen && typeof this.config.onDrawerOpen === 'function' ) { if (!externalCall) { this.config.onDrawerOpen(); } } if (this.$activeSource && this.$activeSource.attr('aria-expanded')) { this.$activeSource.attr('aria-expanded', 'true'); } this.bindEvents(); }; Drawer.prototype.close = function() { // don't close a closed drawer if (!this.drawerIsOpen) { return; } this.$open.removeClass(this.config.openClass); // deselect any focused form elements $(document.activeElement).trigger('blur'); // Ensure closing transition is applied to moved elements, like the nav this.nodes.$moved.prepareTransition({ disableExisting: true }); this.$drawer.prepareTransition({ disableExisting: true }); this.nodes.$parent.removeClass( this.config.dirOpenClass + ' ' + this.config.openClass ); this.drawerIsOpen = false; // Remove focus on drawer slate.a11y.removeTrapFocus({ $container: this.$drawer, namespace: 'drawer_focus' }); if (this.$activeSource && this.$activeSource.attr('aria-expanded')) { this.$activeSource.attr('aria-expanded', 'false'); } this.unbindEvents(); }; Drawer.prototype.bindEvents = function() { // Lock scrolling on mobile this.nodes.$page.on('touchmove.drawer', function() { return false; }); // Clicking out of drawer closes it this.nodes.$page.on( 'click.drawer', $.proxy(function() { this.close(); return false; }, this) ); // This is my own code - swiping left closes it $( '#NavDrawer' ).touchwipe({ wipeLeft: function() { alert("yes, you swiped left on the menu"); // Now what ? }, }); // Pressing escape closes drawer this.nodes.$parent.on( 'keyup.drawer', $.proxy(function(evt) { if (evt.keyCode === 27) { this.close(); } }, this) ); }; Drawer.prototype.unbindEvents = function() { this.nodes.$page.off('.drawer'); this.nodes.$parent.off('.drawer'); }; return Drawer; })(); Any idea how I should tackle this problem ?
How to run 2 js functions
I have 2 function that I am trying to run, one after another. For some reason they both run at the same time, but the second one does not load properly. Is there a way to run the first function wait then run the second function?: //run this first $('#abc').click(function() { $('.test1').show(); return false; }); //run this second (function ($) { "use strict"; // A nice closure for our definitions function getjQueryObject(string) { // Make string a vaild jQuery thing var jqObj = $(""); try { jqObj = $(string) .clone(); } catch (e) { jqObj = $("<span />") .html(string); } return jqObj; } function printFrame(frameWindow, content, options) { // Print the selected window/iframe var def = $.Deferred(); try { frameWindow = frameWindow.contentWindow || frameWindow.contentDocument || frameWindow; var wdoc = frameWindow.document || frameWindow.contentDocument || frameWindow; if(options.doctype) { wdoc.write(options.doctype); } wdoc.write(content); wdoc.close(); var printed = false; var callPrint = function () { if(printed) { return; } // Fix for IE : Allow it to render the iframe frameWindow.focus(); try { // Fix for IE11 - printng the whole page instead of the iframe content if (!frameWindow.document.execCommand('print', false, null)) { // document.execCommand returns false if it failed -http://stackoverflow.com/a/21336448/937891 frameWindow.print(); } // focus body as it is losing focus in iPad and content not getting printed $('body').focus(); } catch (e) { frameWindow.print(); } frameWindow.close(); printed = true; def.resolve(); } // Print once the frame window loads - seems to work for the new-window option but unreliable for the iframe $(frameWindow).on("load", callPrint); // Fallback to printing directly if the frame doesn't fire the load event for whatever reason setTimeout(callPrint, options.timeout); } catch (err) { def.reject(err); } return def; } function printContentInIFrame(content, options) { var $iframe = $(options.iframe + ""); var iframeCount = $iframe.length; if (iframeCount === 0) { // Create a new iFrame if none is given $iframe = $('<iframe height="0" width="0" border="0" wmode="Opaque"/>') .prependTo('body') .css({ "position": "absolute", "top": -999, "left": -999 }); } var frameWindow = $iframe.get(0); return printFrame(frameWindow, content, options) .done(function () { // Success setTimeout(function () { // Wait for IE if (iframeCount === 0) { // Destroy the iframe if created here $iframe.remove(); } }, 1000); }) .fail(function (err) { // Use the pop-up method if iframe fails for some reason console.error("Failed to print from iframe", err); printContentInNewWindow(content, options); }) .always(function () { try { options.deferred.resolve(); } catch (err) { console.warn('Error notifying deferred', err); } }); } function printContentInNewWindow(content, options) { // Open a new window and print selected content var frameWindow = window.open(); return printFrame(frameWindow, content, options) .always(function () { try { options.deferred.resolve(); } catch (err) { console.warn('Error notifying deferred', err); } }); } function isNode(o) { /* http://stackoverflow.com/a/384380/937891 */ return !!(typeof Node === "object" ? o instanceof Node : o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName === "string"); } $.print = $.fn.print = function () { // Print a given set of elements var options, $this, self = this; // console.log("Printing", this, arguments); if (self instanceof $) { // Get the node if it is a jQuery object self = self.get(0); } if (isNode(self)) { // If `this` is a HTML element, i.e. for // $(selector).print() $this = $(self); if (arguments.length > 0) { options = arguments[0]; } } else { if (arguments.length > 0) { // $.print(selector,options) $this = $(arguments[0]); if (isNode($this[0])) { if (arguments.length > 1) { options = arguments[1]; } } else { // $.print(options) options = arguments[0]; $this = $("html"); } } else { // $.print() $this = $("html"); } } // Default options var defaults = { globalStyles: true, mediaPrint: false, stylesheet: null, noPrintSelector: ".no-print", iframe: true, append: null, prepend: null, manuallyCopyFormValues: true, deferred: $.Deferred(), timeout: 750, title: null, doctype: '<!doctype html>' }; // Merge with user-options options = $.extend({}, defaults, (options || {})); var $styles = $(""); if (options.globalStyles) { // Apply the stlyes from the current sheet to the printed page $styles = $("style, link, meta, base, title"); } else if (options.mediaPrint) { // Apply the media-print stylesheet $styles = $("link[media=print]"); } if (options.stylesheet) { // Add a custom stylesheet if given $styles = $.merge($styles, $('<link rel="stylesheet" href="' + options.stylesheet + '">')); } // Create a copy of the element to print var copy = $this.clone(); // Wrap it in a span to get the HTML markup string copy = $("<span/>") .append(copy); // Remove unwanted elements copy.find(options.noPrintSelector) .remove(); // Add in the styles copy.append($styles.clone()); // Update title if (options.title) { var title = $("title", copy); if (title.length === 0) { title = $("<title />"); copy.append(title); } title.text(options.title); } // Appedned content copy.append(getjQueryObject(options.append)); // Prepended content copy.prepend(getjQueryObject(options.prepend)); if (options.manuallyCopyFormValues) { // Manually copy form values into the HTML for printing user-modified input fields // http://stackoverflow.com/a/26707753 copy.find("input") .each(function () { var $field = $(this); if ($field.is("[type='radio']") || $field.is("[type='checkbox']")) { if ($field.prop("checked")) { $field.attr("checked", "checked"); } } else { $field.attr("value", $field.val()); } }); copy.find("select").each(function () { var $field = $(this); $field.find(":selected").attr("selected", "selected"); }); copy.find("textarea").each(function () { // Fix for https://github.com/DoersGuild/jQuery.print/issues/18#issuecomment-96451589 var $field = $(this); $field.text($field.val()); }); } // Get the HTML markup string var content = copy.html(); // Notify with generated markup & cloned elements - useful for logging, etc try { options.deferred.notify('generated_markup', content, copy); } catch (err) { console.warn('Error notifying deferred', err); } // Destroy the copy copy.remove(); if (options.iframe) { // Use an iframe for printing try { printContentInIFrame(content, options); } catch (e) { // Use the pop-up method if iframe fails for some reason console.error("Failed to print from iframe", e.stack, e.message); printContentInNewWindow(content, options); } } else { // Use a new window for printing printContentInNewWindow(content, options); } return this; }; })(jQuery); How would I run the first one wait 5 or so seconds and then run the jquery print? I'm having a hard time with this. So the id would run first and then the print would run adter the id="abc" Here is an example of the code in use: <div id="test"> <button id="abc" class="btn" onclick="jQuery.print(#test1)"></button> </div>
If I understand your problem correctly, you want the jQuery click function to be run first, making a div with id="test1" visible and then, once it's visible, you want to run the onclick code which calls jQuery.print. The very first thing I will suggest is that you don't have two different places where you are handling the click implementation, that can make your code hard to follow. I would replace your $('#abc').click with the following: function printDiv(selector) { $(selector).show(); window.setTimeout(function () { jQuery.print(selector); }, 1); } This function, when called, will call jQuery.show on the passed selector, wait 1ms and then call jQuery.print. If you need the timeout to be longer, just change the 1 to whatever you need. To use the function, update your example html to the following: <div id="test"> <button id="abc" class="btn" onclick="printDiv('#test1')"</button> </div> When the button is clicked, it will now call the previously mentioned function and pass it the ID of the object that you want to print. As far as your second function goes, where you have the comment **//run this second**, you should leave that alone. All it does is extend you jQuery object with the print functionality. You need it to run straight away and it currently does.
How do I capture if the enter key has been pressed?
I'm trying to capture if the enter key has been pressed and execute a search. This is the viewmodel for the search page. (function () { a.viewModels.userSearch = function (view, params) { $view = $(view); var self = a.viewModel({ users: a.collection({ url: '/admin/Account/SearchUsers', query: { SearchText: null } }).fetch(), setPageIndex: setPageIndex, search: search }); $view.keypress(function (e) { if (e.keyCode == 13) { self.search(e); } }); function search(e) { self.users.query.rowCount = 0; self.users.query.pageIndex = 1; self.users.fetch(); } function setPageIndex(e) { e.preventDefault(); self.users.query.set('pageIndex', $(e.currentTarget).data('page-index')); self.users.fetch(); } return self; } Now, this works. The problem is that it works only after pressing the 'Enter' key 2 times. Seems like I'm missing something related to the scope but js ain't my cup of tea. If it is of any help, here goes my view model function: function viewModel(viewModelConfig) { var self = kendo.observable($.extend({ busy: 0, resultMessage: null, clearResultMessage: clearResultMessage }, viewModelConfig)); self.bind('change', onChange); function onChange(change) { var errorProp, errorMsg, infoProp, infoMsg; if (change.field.endsWith('.busy')) { if (self.get(change.field)) self.set('busy', self.busy + 1); else if (self.busy > 0) self.set('busy', self.busy - 1); } else if (change.field.endsWith('.resultMessage')) { var data = self.get(change.field); self.set('resultMessage', data); } } function clearResultMessage(e) { if (e) e.preventDefault(); self.set('resultMessage', null); return false; } return self; }
I have a similar setup on my site, and using MVVM, just add the custom enter binding within the data-bind attribute of the element to link to the function within the view-model you wish to execute. The code to add the custom binder is as such: kendo.data.binders.widget.enter = kendo.data.Binder.extend({ init: function(element, bindings, options) { kendo.data.Binder.fn.init.call(this, element, bindings, options); var binding = this.bindings.enter; $(element.element).keyup(function(e) { if( e.which === 13 ) bindings.get(); }); }, refresh: $.noop });
jquery draggable revert programmatically
I am currently developing a calendar where activities can be drag&dropped to other days. When an activity is dropped into a different day, I show a custom modal using durandal's dialog plugin. The problem is when an user closes the modal, the activity has to revert to its original position. When an activity is dropped the following code is called: function showDroppedActivityModal(obj) { require(['modals/droppedActivityModal/droppedActivityModal', 'moment'], function(droppedActivityModal, moment) { droppedActivityModal.show(obj).then(function(response) { if(response !== false) { ... } // dialog closes else { calendarView.revertActivity.notify({ revert: true}) } }); }); } In my calendarView I implemented the revertActivity event to set revert to true but the function never re-evaluates itself but i'm able to receive the new revert value (true). $(activity).draggable({ revert: function() { var revert = false; self.revertActivity.attach(function(sender, args) { revert = args.revert; }); return revert; } }); Custom event code: function CalendarEvent(sender) { this._sender = sender; this._listeners = []; } CalendarEvent.prototype = { attach : function (listener) { this._listeners.push(listener); }, notify : function (args) { var index; for (index = 0; index < this._listeners.length; index += 1) { this._listeners[index](this._sender, args); } }, remove : function (listener){ this._listeners.remove(listener); } }; this.revertActivity = new CalendarEvent(this);
Making a method in a plugin accessible globally?
Given the jQuery dropdown plugin below. Is there a way to add a method that would allow for a separate function outside of the dropdown to 'hideMenu'? Thanks For example, if I applied the plugin to a div with an ID like so: $('#settings.dropdown').dropDownMenu(); How could I then call to close the dropDownMenu w hideMenu from outside of the plugin? Thanks jQuery.fn.dropDownMenu = function() { // Apply the Dropdown return this.each(function() { var dropdown = $(this), menu = dropdown.next('div.dropdown-menu'), parent = dropdown.parent(); // For keeping track of what's "open" var activeClass = 'dropdown-active', showingDropdown = false, showingMenu, showingParent, opening; // Dropdown Click to Open dropdown.click(function(e) { opening = true; // Track opening so that the body click doesn't close. This allows other js views to bind to the click e.preventDefault(); if (showingDropdown) { dropdown.removeClass(activeClass); parent.removeClass(activeClass); showingMenu.hide(); showingDropdown = false; } else { showingDropdown = true; showingMenu = menu; showingParent = parent; menu.show(); dropdown.addClass(activeClass); parent.addClass(activeClass); } }); // When you click anywhere on the page, we detect if we need to blur the Dropdown Menu $('body').click(function(e) { if (!opening && showingParent) { var parentElement = showingParent[0]; if (!$.contains(parentElement, e.target) || !parentElement == e.target) { hideMenu(); } } opening = false; }); // hides the current menu var hideMenu = function() { if(showingDropdown) { showingDropdown = false; dropdown.removeClass(activeClass); parent.removeClass(activeClass); showingMenu.hide(); } }; }); };
jQuery advises making multiple methods available through the plugin itself: jQuery.fn.dropDownMenu = function(method) { var methods = { init: function() { // Put all your init code here }, hide: function() { hideMenu(); } }; if ( methods[method] ) { return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )); } else if ( typeof method === 'object' || ! method ) { return methods.init.apply( this, arguments ); } else { $.error( 'Method ' + method + ' does not exist on jQuery.tooltip' ); } function hideMenu() { // ... } }; See http://docs.jquery.com/Plugins/Authoring#Plugin_Methods Update: Use like this: // Use the plugin normally to run the init method $('#settings.dropdown').dropDownMenu(); // Call the hide method $('#settings.dropdown').dropDownMenu('hide');
Sure. Give hideMenu to the global window object, like this: window["hideMenu"] = function() { if(showingDropdown) { showingDropdown = false; dropdown.removeClass(activeClass); parent.removeClass(activeClass); showingMenu.hide(); } }; You can then call it as usual anywhere you need to.