Knockout: click other place of the page and hide the element - javascript

I'm creating a 2 level menu, so clicking on the topmenu item , the submenu shows up, if clicking on other places of the page rather than the submenu itself, the submenu should hide. I'm not leaning to adding a click bind to the body tag, it's not going to work anyway, but what can I do to achieve this?
here is my code so far.
<div id="menuholder">
<ul id="topmenu">
<li data-bind="click: showMenu.bind($data, 1)">top menu item</li>
<ul class="submenu" data-bind="visible: selected() == '1'">
<li>submenu item</li>
</ul>
</ul>
</div>
<script type="text/javascript">
var menuModel = function () {
var self = this;
self.selected = ko.observable(0);
self.showMenu = function (data) {
var s = self.selected();
if (s > 0 && data == s)
self.selected(0);
else
self.selected(data);
};
self.hideMenu = function () {
self.selected(0);
}
}
ko.applyBindings(new menuModel(), document.getElementById("menuHolder"));

If you check out how Twitter Bootstrap does its dropdowns, it adds an event to the html element:
Inside Dropdown class definition:
...
$('html').on('click.dropdown.data-api', function () {
$el.parent().removeClass('open')
})
You could try something similar.

$('yourParentDivId').click(function(e) {
if (!( $(e.target).is('topmenu') && $(e.target).is('submenu') ) ) {
alert('clicked');
self.hideMenu();
}
});
Refer Here knockout delegate event

Related

how can i use ONLY bind instead of call when Iterating a NodeList using forEach?

I am new to programming in Javascript .so please explain me can I use binding here.This menu is inspired by the left side menu found on YouTube. When clicking on the menu label and icon, the main menu appears beneath and the menu icon slides to the right side while the label slides up. To close the menu, the menu icon needs to be clicked again.
var menu = (function() {
function initiate() {
//[].slice.call I used by using call but i want to to by binding and I am not able to do.
[].slice.bind(null, document.querySelectorAll('.menu')).forEach(function(element, i) {
var titleclick = el.querySelector('div.d-striker'),
striker.addEventListener('click', function(event) {
if (!open) {
el.className += ' dr-menu-open';
open = true;
}
}, false);
icon.addEventListener('click', function(event) {
if (open) {
event.stopPropagation();
open = false;
el.className = el.className.replace(/\bdr-menu-open\b/, '');
return false;
}
}, false);
}
initiate();
})();
<div class="side">
<nav class="menu">
<div class="d-striker">
<span class="d-icon dr-icon-menu"></span><a class="dr-label">Account</a>
</div>
<ul>
<li><a class="d-icon dr-icon-user" href="#">icon</a></li>
<li><a class="d-icon dr-icon-cam" href="#">Videos</a></li>
<li><a class="d-icon dr-icon-download" href="#">Downloads</a></li>
<li><a class="d-icon dr-icon-settings" href="#">Settings</a></li>
</ul>
</nav>
</div>
bind() returns a bound function, but you need to call that function to get the array that's needed for forEach. Add parentheses to call the function.
[].slice.bind(null, document.querySelectorAll('.menu'))().forEach(function(element, i) {
^^

Change the css of a select link with jquery

I am working on an AJAX pagination and I made it by using this code.
$(document).ready(function() {
var pagination = $("#pagination");
var url = urlProviderOffers;
updateContent(function(json) {});
pagination.on('click', "ul a", function(e) {
e.preventDefault();
var page_to_visit = $(this).text();
updateContent(function(json) {});
pagination.find('ul li').removeClass('active');
$(this).parent().addClass('active');
});
function updateContent(callback, page_to_visit) {
page_to_visit = typeof(page_to_visit) != 'undefined' ? page_to_visit : 1;
$.ajax({
url: url,
data: {
page: page_to_visit
}
}).done(function(json) {
if (json.total > 1) {
pagination.find("ul li:nth-child(1)").addClass('active');
}
callback(json);
});
}
url = template_url.replace(/provider_id_to_change/, providerID);
return url;
}
});
<div id="pagination">
<ul class="pagination">
<li class="active">1
</li>
<li>2
</li>
<li>3
</li>
</ul>
</div>
The problem I am having is that, any time I click on one of the links of the pagination, a function is called and inside this function I change the active link been visited by the user.
For instance, when my page is loading, the function updateContent sets the current page been visited to 1. And after clicking on another link of the pagination, I remove all the added class and add a new active class to the selected link.
In my case anytime, when a link is clicked, is set the class of the selected link to active and automatically remove the added class and set the active class to the first link.
kindly help me solve this problem

How to bind click event to body under certain conditions using knockout.js?

I've designed a dropdown menu (in vain of attempting to style an actual <select> element consistently across browsers), which when clicked, shows an unordered list of dropdown options. This functionality is provided via Knockout.js which uses an observable to check whether the dropdown should be shown or hidden. The DOM structure is here:
<div id="actionsDropdown">
<a data-bind="click: toggleDropdownVisibility, css: { active: showDropdown() == true }">Actions</a>
<ul data-bind="visible: showDropdown">
<li>Option 1</li>
<li>Option 2</li>
</ul>
</div>
And here is my Knockout.js code:
self.showDropdown = ko.observable(false);
self.toggleDropdownVisibility = function () {
console.log(self.showDropdown());
self.showDropdown(!self.showDropdown());
};
This works pretty well, except for a few things.
How can I implement logic to also hide the dropdown when anywhere else on the page is clicked if the dropdown is already visible?
I guess I could bind a click handler to the body element, but then it'd open it if it were closed, which is obviously not optimal.
See this jsfiddle:
http://jsfiddle.net/dwp0etrg/9/
I just created a quick custom binding handler that accepts takes showDropdown and adds an event listener to document. When showDropdown in true, it binds the listener and when its false, it removes it.
ko.bindingHandlers.dropdown = {
update: function (element, valueAccessor) {
var value = valueAccessor();
var valueUnwrapped = ko.unwrap(value);
if (valueUnwrapped) {
$(document).on('click.dropdown', function (e) {
var $target = $(e.target);
if ($target.parents('#actionsDropdown').length === 0) {
value(false);
}
});
} else {
$(document).off('click.dropdown');
}
}
};
Just change your template to use the binding and pass it showDropdown observable.
<div id="actionsDropdown">
<a data-bind="dropdown: showDropdown,
click: toggleDropdownVisibility,
css: { active: showDropdown() == true }">Actions</a>
<ul data-bind="visible: showDropdown">
<li>Option 1</li>
<li>Option 2</li>
</ul>
</div>

addClass, removeClass, preventDefault

I want to removeClass "active" form all childelements and addCLass "active" to one childelement, but when I use event.preventDefault,the link in the element will not longer work, if I remove the event.preventDefault, the addClass will work only in very short time and it will go back to the default "active" class. The html code:
<div id="menubar">
<ul class="nav navbar-nav">
<li class="active">m1</li>
<li >m2</li>
<li >m3</li>
<li >m4</li>
<li >m5</li>
</ul>
</div>
and JavaScript:
$(document).ready(function(){
$("#menubar li").click(function(e) {
e.preventDefault();
$("#menubar li").removeClass("active")
$(this).addClass("active")
});
})
what should I do to enable the links?
If you are doing this in .Net you could create navagation bar using a menu object in the master page and add this to the code behind
string ThisPage = Page.AppRelativeVirtualPath;
foreach (MenuItem item in Menu1.Items)
{
if (item.NavigateUrl == ThisPage)
{
item.Selected = true;
item.Selectable = true;
}
}
foreach (MenuItem item in MenuAdmin.Items)
{
if (item.NavigateUrl == ThisPage)
{
item.Selected = true;
item.Selectable = true;
}
}
You need the backend to set this in order for it to 'stick'. Otherwise following a link resets the javascript.

More confusing behavior with stopPropagation()

I have the following code It takes a <ul> and it drops down the contents when clicked. That works great. The problem was that it would also close other menus of the same type when a child <li> was clicked.
I 'fixed' this problem by using the if clause to determine if the item being clicked was also the item that was currently open, but I want to take it a step further and make it so that if the parent ul is clicked again, it will close the menu. I am having a great deal of misunderstanding as to how to approach this. I attempted to stop the propagation of the children elements, but it yields the same results. Can anyone assist?
wiring (document load)
$(document).ready(function () {
$('[data-role="sidebar-dropdown"]').drawer({
open: 'sidebar-dropdown-open',
css: '.sidebar-dropdown-open'
});
});
html
<ul>
<li class=" dropdown" data-role="sidebar-dropdown">
Link Text
<ul class="sub-menu light sidebar-dropdown-menu">
<li><a class="remote" href="pages/...">Link Text</a></li>
<li><a class="remote" href="pages/...">Link Text</a></li>
<li><a class="remote" href="pages/...">Link Text</a></li>
</ul>
</li>
</ul>
JavaScript
(function ($) {
$.fn.drawer = function (options) {
// Create some defaults, extending them with any options that were provided
var settings = $.extend({
open: 'open',
css: '.open'
}, options);
return this.each(function () {
$(this).on('click', function (e) {
// slide up all open dropdown menus
$(settings.css).not($(this)).each(function () {
$(this).removeClass(settings.open);
// retrieve the appropriate menu item
var $menu = $(this).children(".dropdown-menu, .sidebar-dropdown-menu");
// slide down the one clicked on.
$menu.slideUp('fast');
$menu.removeClass('active');
});
// mark this menu as open
$(this).addClass(settings.open);
// retrieve the appropriate menu item
var $menu = $(this).children(".dropdown-menu, .sidebar-dropdown-menu");
// slide down the one clicked on.
$menu.slideDown(100);
$menu.addClass('active');
e.preventDefault();
e.stopPropagation();
}).on("mouseleave", function () {
$(this).children(".dropdown-menu").hide().delay(300);
});
})
};
})(jQuery);
In jQuery events you can read the node which initiated the event by referencing e.target.
if ($(e.target).is("li")) { // do something only if the clicked element was a li }
Regarding closing elements which are not children, instead of doing a global selector $(selector) you should instead do a selector relative to your initiating dom node. It's a common practice to pass this and stashing it inside your jQuery plugin.
return this.each(function(this)) { var $node = $(this); }
Then all lookups would be done like
$node.find(selector).doStuff()

Categories