Custom jQuery Dropdown not firing with appended options - javascript

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!

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/

Refactoring repeated code in javascript prototype constructor

I have a open function that once triggered, simply plays video in a dedicated panel.
This function can be triggered in two ways - one with a click and another one with a page load (window load) with url that contains a valid anchor tag.
They all work fine but some codes of the window load handler are repetitive and I'm not too sure how I can keep this DRY.
Please take a look and point me in some directions on how I can write this better.
I commented in open function which is for which.
$.videoWatch.prototype = {
init: function() {
this.$openLinks = this.$element.find(".open");
this.$closeLinks = this.$element.find(".close");
this.open();
this.close();
},
_getContent: function(element) {
var $parent = element.parent(),
id = element.attr('href').substring(1),
title = $parent.data('title'),
desc = $parent.data('desc');
return {
title: title,
desc: desc,
id: id
}
},
open: function() {
var self = this;
//open theatre with window load with #hash id
window.onload = function() {
var hash = location.hash;
var $a = $('a[href="' + hash + '"]'),
content = self._getContent($a),
$li = $a.parents("li"),
$theatreVideo = $(".playing"),
$theatreTitle = $(".theatre-title"),
$theatreText = $(".theatre-text");
$(".theatre").attr('id', content.id);
$theatreTitle.text(content.title);
$theatreText.text(content.desc);
if ($theatreText.text().length >= 90) {
$(".theatre-text").css({
'overflow': 'hidden',
'max-height': '90px',
});
$moreButton.insertAfter($theatreText);
}
$a.parent().addClass("active");
$(".theatre").insertAfter($li);
$(".theatre").slideDown('fast', scrollToTheatre);
oldIndex = $li.index();
}
//open theatre with click event
self.$openLinks.on("click", function(e) {
// e.preventDefault();
if (curID == $(this).parent().attr("id")) {
$("figure").removeClass("active");
$("button.more").remove();
$(".theatre").slideUp('fast');
$('.playing').attr("src", "");
removeHash();
oldIndex = -1;
curID = "";
return false
} else {
curID = $(this).parent().attr("id");
}
var $a = $(this),
content = self._getContent($a),
$li = $a.parents("li"),
$theatreVideo = $(".playing"),
$theatreTitle = $(".theatre-title"),
$theatreText = $(".theatre-text");
$(".theatre").attr('id', content.id);
$theatreTitle.text(content.title);
$theatreText.text(content.desc);
if ($theatreText.text().length >= 90) {
$(".theatre-text").css({
'overflow': 'hidden',
'max-height': '90px',
});
$moreButton.insertAfter($theatreText);
}
if (!($li.index() == oldIndex)) {
$("figure").removeClass("active");
$(".theatre").hide(function(){
$a.parent().addClass("active");
$(".theatre").insertAfter($li);
$(".theatre").slideDown('fast', scrollToTheatre);
oldIndex = $li.index();
});
} else {
$(".theatre").insertAfter($li);
scrollToTheatre();
$("figure").removeClass("active");
$a.parent().addClass("active");
}
});
},
...
Simplified and refactored open method:
open: function() {
var self = this;
var serviceObj = {
theatreVideo : $(".playing"),
theatre: $(".theatre"),
theatreTitle : $(".theatre-title"),
theatreText : $(".theatre-text"),
setTheatreContent: function(content){
this.theatre.attr('id', content.id);
this.theatreTitle.text(content.title);
this.theatreText.text(content.desc);
if (this.theatreText.text().length >= 90) {
this.theatreText.css({
'overflow': 'hidden',
'max-height': '90px',
});
$moreButton.insertAfter(this.theatreText);
}
},
activateTeatre: function(a, li){
a.parent().addClass("active");
this.theatre.insertAfter(li);
this.theatre.slideDown('fast', scrollToTheatre);
oldIndex = li.index();
}
};
//open theatre with window load with #hash id
window.onload = function() {
var hash = location.hash;
var $a = $('a[href="' + hash + '"]'),
content = self._getContent($a),
$li = $a.parents("li");
serviceObj.setTheatreContent(content);
serviceObj.activateTeatre($a, $li);
}
//open theatre with click event
self.$openLinks.on("click", function(e) {
// e.preventDefault();
if (curID == $(this).parent().attr("id")) {
$("figure").removeClass("active");
$("button.more").remove();
$(".theatre").slideUp('fast');
$('.playing').attr("src", "");
removeHash();
oldIndex = -1;
curID = "";
return false
} else {
curID = $(this).parent().attr("id");
}
var $a = $(this),
content = self._getContent($a),
$li = $a.parents("li");
serviceObj.setTheatreContent(content);
if (!($li.index() == oldIndex)) {
$("figure").removeClass("active");
$(".theatre").hide(function(){
serviceObj.activateTeatre($a, $li);
});
} else {
$(".theatre").insertAfter($li);
scrollToTheatre();
$("figure").removeClass("active");
$a.parent().addClass("active");
}
});
},
1st of all there are variables that don't depend on the input, you could pull them to the class (I'll show just one example, as you asked for directions):
init: function() {
this.$theatreVideo = $(".playing");
All the variables that do depend on the input, like $li could be moved to a function:
var $a = $(this),
$dependsOnA = self.dependsOnA($a);
self.actionDependsOnA($dependsOnA); // see below
function dependsOnA($a) {
return {
a: $a,
li: $a.parents("li"),
content: self._getContent($a)
}
}
Also the code that "repeats" can be moved to a function:
function actionDependsOnA($dependsOnA)
$(".theatre").attr('id', $dependsOnA.content.id);
$theatreTitle.text($dependsOnA.content.title);
$theatreText.text($dependsOnA.content.desc);
}

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'));

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');
});

Make JS menu show on hover rather than on click

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.

Categories