Make JS menu show on hover rather than on click - javascript

I've got Dropdown Menu 3 from here: http://tympanus.net/codrops/2012/10/04/custom-drop-down-list-styling/
Working well onclick, but I don't know how to make it work on hover.
My attempt at the JS:
<script type="text/javascript">
function DropDown(el) {
this.dd = el;
this.placeholder = this.dd.children('span');
this.opts = this.dd.find('ul.dropdown > a');
this.val = '';
this.index = -1;
this.initEvents();
}
DropDown.prototype = {
initEvents : function() {
var obj = this;
obj.dd.on('hover', function(event){
$(this).toggleClass('active');
return false;
});
obj.opts.on('hover',function(){
var opt = $(this);
obj.val = opt.text();
obj.index = opt.index();
obj.placeholder.text(obj.val);
});
},
getValue : function() {
return this.val;
},
getIndex : function() {
return this.index;
}
}
$(function() {
var dd = new DropDown( $('#dd') );
$(document).hover(function() {
// all dropdowns
$('.wrapper-dropdown-3').removeClass('active');
});
});
</script>

I think this should work:
$(function() {
var dd = new DropDown( $('#dd') );
$('.wrapper-dropdown-3').hover(function() {
$(this).toggleClass('active');
});
});
If got any issues, say so.

Try:
obj.dd.on('mouseenter', function(event){
$(this).toggleClass('active');
return false;
});
obj.opts.on('mouseleave',function(){
var opt = $(this);
obj.val = opt.text();
obj.index = opt.index();
obj.placeholder.text(obj.val);
});

If I am not mistaken, the hover() function looks for mouseover and mouseoff events. Why would you want the menu to only change when your mouse is over an item at the top? Once you mouseoff it will change. I recommend using just mouseover, and then a mouseleave instead of hover.

Related

jQ Conditional handling of several separated events

Is there a way to handle separated events by one condition, without checking it in every event handler function? I.e. in the code listed below. Or maybe there is a way to do it more convinient? Any thoughts/suggestions?
var Obj = function(){
var self = this;
this.initialized = true;
$(document).on('click', function(){
if(self.initialized){
alert('hax!');
self.initialized = false;
}
})
$('input').on('mouseenter mouseleave', function(e){
if(self.initialized){
$(this).attr('class', e.type == 'mouseenter' ? 'hovered' : '');
}
})
/*$('...').on(...) etc etc*/
}
var obj = new Obj()
.hovered{
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type='button' value='Click me!'/>
i've played a bit around and maybe this is a working solution for you:
eventHandlerWrapper = function(state, callback) {
return function(e) {
if(state.initialized) {
return callback.bind(this, e)();
}
}
}
var Obj = function() {
this.initialized = true;
}
var obj = new Obj()
$(document).on('click', eventHandlerWrapper(obj, function(e){
obj.initialized = false;
}))
$('input').on('mouseenter mouseleave', eventHandlerWrapper(obj, function(e){
$(e.currentTarget).attr('class', e.type == 'mouseenter' ? 'hovered' : '');
}))
I've also create a fiddle: https://jsfiddle.net/jz17L7h6/
Can you add another class for your button?
var Obj = function(){
var self = this;
self.initialized = true;
self.dom = $('<input>').attr('type', 'button').val('Click me!')
.data('obj', this)
.appendTo($('.container'));
/*$('...').on(...) etc etc*/
}
$(document)
.on('click', 'input:not(.uninitialized)', function(){
alert('hax!');
$(this).addClass('uninitialized')
.data('obj').initialized = false;
})
.on('mouseenter mouseleave', 'input:not(.uninitialized)', function(e){
$(this).attr('class', e.type == 'mouseenter' ? 'hovered' : '');
});
for (var i = 0; i < 5; i++) {
new Obj();
}
https://jsfiddle.net/chukanov/fzez5th2/3/
If you can't add a class, you can turn off your listeners
var Obj = function(){
var self = this;
self.initialized = true;
self.dom = $('<input>').attr('type', 'button').val('Click me!')
.appendTo($('.container'));
var moveHandler = function(e){
$(this).attr('class', e.type == 'mouseenter' ? 'hovered' : '');
};
var clickHandler = function(){
alert('hax!');
self.initialized = false;
self.dom
.off('mouseenter mouseleave', moveHandler)
.off('click', clickHandler);
};
self.dom
.on('mouseenter mouseleave', moveHandler)
.on('click', clickHandler);
/*$('...').on(...) etc etc*/
}
for (var i = 0; i < 5; i++) {
new Obj();
}
https://jsfiddle.net/chukanov/fzez5th2/4/
Or you can override your handlers
var Obj = function(){
var self = this;
self.initialized = true;
self.dom = $('<input>').attr('type', 'button').val('Click me!')
.appendTo($('.container'));
self.moveHandler = function(e){
$(this).attr('class', e.type == 'mouseenter' ? 'hovered' : '');
};
self.clickHandler = function(e){
alert('hax!');
self.initialized = false;
self.clickHandler = function (e) {};
self.moveHandler = function (e) {};
};
self.dom
.on('mouseenter mouseleave', function (e) { self.moveHandler.apply(this, arguments); })
.on('click', function (e) { self.clickHandler(e); });
/*$('...').on(...) etc etc*/
}
for (var i = 0; i < 5; i++) {
new Obj();
}
https://jsfiddle.net/chukanov/fzez5th2/5/
Update
I think you need to separate mouse events handlers.
var Obj = function(){
var self = this;
self.initialized = true;
self.dom = $('<input>').attr('type', 'button').val('Click me!')
.appendTo($('.container'));
var handlers = {
mouseenter: function (e) {
$(this).addClass('hovered');
},
mouseleave: function (e) {
$(this).removeClass('hovered');
}
}
var moveHandler = function(e){
handlers[e.type].apply(this, arguments);
};
var clickHandler = function(e){
alert('hax!');
self.initialized = false;
clickHandler = function (e) {};
moveHandler = function (e) {};
};
self.dom
.on('mouseenter mouseleave', function (e) {
moveHandler.apply(this, arguments);
})
.on('click', function (e) {
clickHandler(e);
});
/*$('...').on(...) etc etc*/
}
for (var i = 0; i < 5; i++) {
new Obj();
}
https://jsfiddle.net/chukanov/fzez5th2/7/

Nothing happend on click - jQuery

I have this code:
jQuery.fn.extend({
SelectBox: function(options) {
return this.each(function() {
new jQuery.SelectBox(this, options);
});
}
});
jQuery.SelectBox = function(selectobj, options) {
var opt = options || {};
opt.inputClass = opt.inputClass || "inputClass";
opt.containerClass = opt.containerClass || "containerClass";
var inFocus = false;
var $select = $(selectobj);
var $container = setupContainer(opt);
var $input = setupInput(opt);
$select.hide();
hideMe();
$input
.click(function(){
if (!inFocus) {
showMe();
} else {
hideMe();
}
})
.keydown(function(event) {
switch(event.keyCode) {
case 27:
hideMe();
break;
}
})
.blur(function() {
if ($container.not(':visible')) {
hideMe();
}
});
function showMe() {
$container.show();
inFocus = true;
}
function hideMe() {
$container.hide();
inFocus = false;
}
function setupContainer(options){
$container = $("." + options.containerClass);
$input = $("." + options.inputClass);
var first = false;
var li = "";
$select.find('option').each(function(){
if($(this).is(':selected')){
$input.find('span').text($(this).text());
first = true;
}
//var $li = $container.find('ul').append('<li>' + $(this).text() + '</li>');
var li = document.createElement('li');
li.innerHTML = $(this).text();
$container.find('ul').append(li);
$(li).click(function(event){
$(li).remove();
});
});
return $container;
}
function setupInput(options){
$input = $("." + options.inputClass);
$input.attr('tabindex', '0');
return $input;
}
};
In this code, the "select" i choose hidden, and replaced by a list.
Now, i want click on some "li", and "li" removed.
But, i click on the "li" i have created, and nothing happened.
Why? how can i remove or do anything else when i click on the "li"?
There is no need to add event to each individual element. Use event delegation.
$(document).ready(function() {
$(commonParentSelector).on('click', 'li', function() {
// ^^^^^^^^^^^^^^^^^^^^
$(this).remove();
});
});
Update commonParentSelector according to your needs and it should work.
Docs: https://api.jquery.com/on
use var $li = $("<li>").text($(this).text()).appendTo($container.find('ul'));

jQuery search toggle

on my website I have a div .toggle-search that if you click on it it expands to .search-expand where a search form is. This is the code in jQuery
/* Toggle header search
/* ------------------------------------ */
$('.toggle-search').click(function(){
$('.toggle-search').toggleClass('active');
$('.search-expand').fadeToggle(250);
setTimeout(function(){
$('.search-expand input').focus();
}, 300);
});
Now the only way to close the .search-expand is to click once again on the .toggle-search. But I want to change that it closes if you click anywhere else on the site. For an easier example I have the Hueman theme, and I'm talking about the top right corner search option. http://demo.alxmedia.se/hueman/
Thanks!
Add the event on all elements except the search area.
$('body *:not(".search-expand")').click(function(){
$('.toggle-search').removeClass('active');
$('.search-expand').fadeOut(250);
});
or another way,
$('body').click(function(e){
if(e.target.className.indexOf('search-expand') < 0){
$('.toggle-search').removeClass('active');
$('.search-expand').fadeOut(250);
}
});
var isSearchFieldOpen = false;
var $toggleSearch = $('.toggle-search');
var $searchExpand = $('.search-expand');
function toggleSearch() {
// Reverse state
isSearchFieldOpen = !isSearchFieldOpen;
$toggleSearch.toggleClass('active');
// You can use callback function instead of using setTimeout
$searchExpand.fadeToggle(250, function() {
if (isSearchFieldOpen) {
$searchExpand.find('input').focus();
}
});
}
$toggleSearch.on('click', function(e) {
e.stopPropagation();
toggleSearch();
});
$(document.body).on('click', function(e) {
if (isSearchFieldOpen) {
var target = e.target;
// Checking if user clicks outside .search-expand
if (!$searchExpand.is(target) && !$searchExpand.has(target).length) {
toggleSearch();
}
}
});
I have a second search on the site with the same code as before only
with div .toggle-serach2 and .expand-search2, how can i make your code
so it wont overlap. just changing the name to $('toggle-search2')
doesn't cut it
in that case, I would suggest you convert your code into a plugin:
(function($, document) {
var bodyHandlerAttached = false;
var openedForms = [];
var instances = {};
var defaults = {
activeClass: 'active'
};
function ToggleSearch(elem, options) {
this.options = $.extend({}, defaults, options);
this.$elem = $(elem);
this.$btn = $(options.toggleBtn);
this.isOpen = false;
this.id = generateId();
this.bindEvents();
instances[this.id] = this;
if (!bodyHandlerAttached) {
handleOutsideClick();
bodyHandlerAttached = true;
}
}
ToggleSearch.prototype = {
bindEvents: function() {
this.$btn.on('click', $.proxy(toggleHandler, this));
},
open: function() {
if (this.isOpen) { return; }
var _this = this;
this.$btn.addClass(this.options.activeClass);
this.$elem.fadeIn(250, function() {
_this.$elem.find('input').focus();
});
openedForms.push(this.id);
this.isOpen = true;
},
close: function(instantly) {
if (!this.isOpen) { return; }
this.$btn.removeClass(this.options.activeClass);
if (instantly) {
this.$elem.hide();
} else {
this.$elem.fadeOut(250);
}
openedForms.splice(openedForms.indexOf(this.id), 1);
this.isOpen = false;
},
toggle: function() {
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
};
var toggleHandler = function(ev) {
ev.stopPropagation();
this.toggle();
};
var handleOutsideClick = function(e) {
$(document.body).on('click', function(e) {
if (openedForms.length) {
var target = e.target;
var instance;
for (var id in instances) {
instance = instances[id];
if (!instance.$elem.is(target) && !instance.$elem.has(target).length) {
instance.close(true);
}
}
}
});
};
function generateId() {
return Math.random().toString(36).substr(2, 8);
}
$.fn.toggleSearch = function(options) {
return this.each(function() {
if (!$.data(this, 'toggleSearch')) {
$.data(this, 'toggleSearch', new ToggleSearch(this, options));
}
});
};
})(window.jQuery, document);
And then use it like this:
$('.search-expand').toggleSearch({
toggleBtn: '.toggle-search'
});
$('.search-expand2').toggleSearch({
toggleBtn: '.toggle-search2'
});
JSFiddle example
You could add a click handler to the main window that removes the active class:
$(window).click(function(){
$('.toggle-search').removeClass('active');
}
and then prevent the class removal when you click inside of your toggle-search elem
$('.toggle-search').click(function(e){
e.stopPropagation();
// remainder of click code here
)};
Try to add body click listener
$('body').click(function(e){
if ($(e.target).is('.toggle-search')) return;
$('.toggle-search').removeClass('active');
$('.search-expand').fadeOut(250);
});

Custom jQuery Dropdown not firing with appended options

sorry if this is a simple one, I'm very new to jQuery.
I'm using a custom dropdown in place of select boxes. Here is the script:
function DropDown(el) {
this.dd = el;
this.parentid = this.dd.attr('id');
this.placeholder = this.dd.children('span');
this.opts = this.dd.find('ul.dropdown > li');
this.val = '';
this.index = -1;
this.initEvents();
}
DropDown.prototype = {
initEvents : function() {
var obj = this;
obj.dd.on('click', function(event){
$(this).toggleClass('active');
return false;
});
obj.opts.on('click',function(){
var opt = $(this);
obj.val = opt.text();
obj.index = opt.index();
if(obj.parentid == "dd"){ //brand
$('#brand').val(obj.val);
$("#brand").trigger('change')
} else if (obj.parentid == "dd3") { //availibility
$('#availibility').val(obj.val);
$("#availibility").trigger('change')
}
obj.placeholder.text(obj.val);
});
},
getValue : function() {
return this.val;
},
getIndex : function() {
return this.index;
}
}
$(function() {
var dd = new DropDown( $('#dd') );
var dd2 = new DropDown( $('#dd2') );
var dd3 = new DropDown( $('#dd3') );
$(document).click(function() {
// all dropdowns
$('.wrapper-dropdown-1').removeClass('active');
});
});
This works perfectly fine, but one of the dropdowns '#dd2' has different options added to it via .append()
The click event doesn't fire on these new options - any suggestions..?
I think "obj.opts.on('click',function(){...})" is the problem;
I prefer
obj.dd.on('click', '.ul.dropdown > li', function() {
//do your job here!
var opt = $(this);
obj.val = opt.text();
obj.index = opt.index();
if(obj.parentid == "dd"){ //brand
$('#brand').val(obj.val);
$("#brand").trigger('change')
} else if (obj.parentid == "dd3") { //availibility
$('#availibility').val(obj.val);
$("#availibility").trigger('change')
}
obj.placeholder.text(obj.val);
});
because dynamic elment is not binding any event on it!

Change javascript to be static

I have following javascript code to give me drop down menus. I don't want it to drop down the menu. I want it to show all the menu by default what should I remove. Please help.
function DropDown(el) {
this.dd = el;
this.placeholder = this.dd.children('span');
this.opts = this.dd.find('ul.dropdown > li');
this.val = '';
this.index = -1;
this.initEvents();
}
DropDown.prototype = {
initEvents: function () {
var obj = this;
obj.dd.on('click', function (event) {
$(this).toggleClass('active');
return false;
});
obj.opts.on('click', function () {
var opt = $(this);
obj.val = opt.text();
obj.index = opt.index();
obj.placeholder.text(obj.val);
});
},
getValue: function () {
return this.val;
},
getIndex: function () {
return this.index;
}
}
$(function () {
var dd = new DropDown($('#dd'));
$(document).click(function () {
// all dropdowns
$('.wrapper-dropdown-3').removeClass('active');
});
});
remove
obj.dd.on('click', function(event){
$(this).toggleClass('active');
return false;
});
update
and add the class active by changing var dd = new DropDown( $('#dd') ); into var dd = new DropDown( $('#dd').addClass('active') );
also if you don't want it to get hidden onclick of the document, remove the following as well.
$(document).click(function() {
// all dropdowns
$('.wrapper-dropdown-3').removeClass('active');
});

Categories