I have a simple drag / drop script that I am playing around with to learn.. to try the code, just have an element with id "TEST" on a page and place the script on the page.
The element will begin dragging, and when you mouse up it seems like the removeEventListener doesn't seem to be working. I've been messing with it for 2 hours please help! Is there any obvious reason that it is not working? here is the script:
var Example = function() {
var exa = this;
this.elem = null;
this.init = function() {
exa.elem = document.getElementById('TEST');
console.log('exa.init()');
exa.attachEvent(exa.elem, 'mousedown', function(event) {
console.log('mousedown');
exa.drag.anchor(event);
});
}
this.attachEvent = function ( object, event, handler ) {
if (window.attachEvent) {
object.attachEvent( 'on'+event, function() {
handler.apply(object, arguments);
}, false );
} else {
object.addEventListener( event, function() {
handler.apply(object, arguments);
}, false );
}
}
this.detachEvent = function( object, event, handler ){
if (window.detachEvent) {
object.detachEvent( 'on'+event, function(){
handler.apply(object, arguments);
}, false ) ;
} else {
object.removeEventListener( event, function() {
handler.apply(object, arguments);
}, false );
}
}
this.drag = {
'release' : function(event) {
exa.elem.removeEventListener('mousemove', function(event) { exa.drag.move(event) }, true);
console.log('drag.release2');
},
'anchor' : function(event){
console.log('exa.drag.anchor();');
offY= event.clientY-parseInt(exa.elem.offsetTop);
offX= event.clientX-parseInt(exa.elem.offsetLeft);
exa.attachEvent(window, 'mousemove', function(event) {
exa.drag.move(event);
});
},
'move' : function(event) {
exa.elem.style.position = 'absolute';
var topPosition = (event.clientY-offY);
var leftPosition = (event.clientX-offX);
exa.elem.style.top = topPosition+ 'px';
exa.elem.style.left = leftPosition + 'px';
//console.log('FROM THE TOP: ' + topPosition);
//console.log('FROM THE LEFT: ' + leftPosition);
exa.attachEvent(window, 'mouseup', function(event) {
exa.drag.release(event);
});
}
}
}
var example = new Example();
example.attachEvent(window, 'load', function(event) {
example.init(event);
});
Sorry about that, the code I posted had confusing names for the functions and a couple mistakes, please look at the following:
var Example = function() {
var exa = this;
this.elem = null;
this.init = function() {
exa.elem = document.getElementById('TEST');
console.log('exa.init()');
exa.newEvent(exa.elem, 'mousedown', function(event) {
console.log('mousedown');
exa.drag.anchor(event);
});
}
this.newEvent = function ( object, event, handler ) {
if (window.attachEvent) {
object.attachEvent( 'on'+event, function() {
handler.apply(object, arguments);
}, false );
} else {
object.addEventListener( event, function() {
handler.apply(object, arguments);
}, false );
}
}
this.removeEvent = function( object, event, handler ){
if (window.detachEvent) {
object.detachEvent( 'on'+event, function(){
handler.apply(object, arguments);
}, false ) ;
} else {
object.removeEventListener( event, function() {
handler.apply(object, arguments);
}, false );
}
}
this.drag = {
'release' : function(event) {
exa.removeEvent(exa.elem, 'mousemove', exa.drag.move);
console.log('drag.release2');
},
'anchor' : function(event){
console.log('exa.drag.anchor();');
offY= event.clientY-parseInt(exa.elem.offsetTop);
offX= event.clientX-parseInt(exa.elem.offsetLeft);
exa.newEvent(window, 'mousemove', function(event) {
exa.drag.move(event);
});
},
'move' : function(event) {
exa.elem.style.position = 'absolute';
var topPosition = (event.clientY-offY);
var leftPosition = (event.clientX-offX);
exa.elem.style.top = topPosition+ 'px';
exa.elem.style.left = leftPosition + 'px';
exa.newEvent(window, 'mouseup', function(event) {
exa.drag.release(event);
});
}
}
}
var example = new Example();
example.newEvent(window, 'load', function(event) {
example.init(event);
});
You need a variable that refers to first function (callback passed to addEventListener), each time when you passing function with body as argument to removeEventListener, new function is created
var callback = function()
{
alert(1);
}
button.addEventListener('click', callback);
button.removeEventListener('click', callback);
Related
new_line=function()
{
var conteiner=document.createElement('div');
conteiner.classList.add('conteiner');
conteiner.addEventListener('click', function (event){on_click(event, conteiner);}, false);//this is an anonymous function so it is always new
document.body.appendChild(conteiner);
create_el(conteiner);
};
I have this function and in it addEventListener. I know that when I'm giving: function (event){on_click(event, conteiner);} I can't remove this EventListener, but I need give on_click function second parameter.
delete_conteiner=function(which)
{
which.removeEventListener("click", function (event){on_click(event, conteiner);}, false);//this isn't working
document.body.removeChild(which);
};
Is it posible to add EventListener, which execute on_click, and than remove it in oder EventListener? Here full code:
(function()
{
var create_ul, user_names, menage_ul, open, look_for, save_as, user_name, real_name, on_click, set_up, create_el, show_hide_btn_click, delete_el, on_load, get_object_to_save, change_all, save, delete_conteiner, change_see_able, restart, new_line, new_cubes, scroll_with_ctrl;
create_el=function(where, options)
{
var configs=options || {
class_names:'textbox',
value:''
};
var block=document.createElement('textarea');
block.className=configs.class_names;
block.value=configs.value;
where.appendChild(block);
};
delete_el=function(where, which)
{
where.removeChild(which);
};
delete_conteiner=function(which)
{
which.removeEventListener("click", function(event){on_click(event, conteiner);}, false);//I know that won't work
document.body.removeChild(which);
};
change_see_able=function(where)
{
var is_see_able=true;
where.classList.forEach(function(str){
if(str=='none_see_able')
{
is_see_able=false;
}
});
if(is_see_able)
{
where.classList.add('none_see_able');
}
else
{
where.classList.remove('none_see_able');
}
};
on_click=function(ev, conteiner)
{
var is_box_cliked=false;
ev.target.classList.forEach(function(className){
if(className=='textbox')
{
is_box_cliked=true;
}
});
if(is_box_cliked)
{
if(ev.shiftKey)
{
if(!ev.ctrlKey)
{
if(!ev.altKey)
{
create_el(conteiner);
}
}
}
else if(ev.ctrlKey)
{
if(!ev.altKey)
{
delete_el(conteiner, ev.target);
}
}
else if(ev.altKey)
{
change_see_able(ev.target);
}
else
{}
}
else
{
if(ev.ctrlKey)
{
delete_conteiner(conteiner);
}
else
{
if((conteiner.querySelector('.textbox')==null) || ev.shiftKey)
{
create_el(conteiner);
}
}
}
};
get_object_to_save=function()
{
var conteiners=document.body.querySelectorAll('.conteiner'), conteiners_to_save=Array();
conteiners.forEach(function(textboxes){
var textboxes=textboxes.querySelectorAll('.textbox'), textboxes_to_save=Array();
textboxes.forEach(function(textbox){
var textbox_to_save={
class_names:textbox.className,
value:textbox.value
};
textboxes_to_save.push(textbox_to_save);
});
conteiners_to_save.push(textboxes_to_save);
});
return [user_name, JSON.stringify(conteiners_to_save)];
};
new_line=function()
{
var conteiner=document.createElement('div');
conteiner.classList.add('conteiner');
create_el(conteiner);
conteiner.addEventListener('click', function(event){on_click(event, conteiner);}, false);
document.body.appendChild(conteiner);
};
load=function(conteiners)
{
if(conteiners.length===0)
{
new_line();
}
else
{
conteiners.forEach(function(textboxes){
var conteiner=document.createElement('div');
conteiner.classList.add('conteiner');
conteiner.addEventListener('click', function(event){
on_click(event, conteiner);
}, false);
document.body.appendChild(conteiner);
textboxes.forEach(function(options_for_textbox){
create_el(conteiner, options_for_textbox);
});
});
}
}
save=function()
{
localStorage.setItem(real_name, JSON.stringify(get_object_to_save()));
};
look_for=function()
{
for(i=localStorage.length-1; i>=0; i--)
{
if(localStorage.key(i)!='name_last')
{
if(user_name===JSON.parse(localStorage.getItem(localStorage.key(i)))[0])
{
return [true, localStorage.key(i)];
}
}
}
user_names.push(user_name);
localStorage.setItem('names', JSON.stringify(user_names));
return [false, 'c_'+new Date().getTime()];
};
open=function()
{
if(document.querySelector('.buttons > .open > input').value!='')
{
var conteiners=document.querySelectorAll('.conteiner');
conteiners.forEach(function(conteiner)
{
delete_conteiner(conteiner);
});
user_name=document.querySelector('.buttons > .open > input').value;
document.querySelector('.buttons > .open > input').value='';
var is=look_for();
real_name=is[1];
localStorage.setItem('name_last', real_name);
if(!is[0])
{
localStorage.setItem(real_name, JSON.stringify([user_name, JSON.stringify(new Array())]));
}
conteiners=JSON.parse(JSON.parse(localStorage.getItem(real_name))[1]);
load(conteiners);
}
};
save_as=function()
{
if(document.querySelector('.buttons > .save_as > input').value!='')
{
user_name=document.querySelector('.buttons > .save_as > input').value;
document.querySelector('.buttons > .save_as > input').value='';
real_name=look_for();
localStorage.setItem('name_last', real_name);
localStorage.setItem(real_name, JSON.stringify(get_object_to_save()));
}
};
on_load=function()
{
var name=localStorage.getItem('name_last');
if(localStorage.length===0)
{
user_names=new Array();
localStorage.setItem('names', JSON.stringify(new Array()));
user_name='first';
real_name=look_for()[1];
localStorage.setItem('name_last', real_name);
localStorage.setItem(real_name, JSON.stringify([user_name, JSON.stringify(new Array())]));
}
else
{
real_name=name;
user_name=JSON.parse(localStorage.getItem(real_name))[0];
user_names=JSON.parse(localStorage.getItem('names'));
}
var conteiners=JSON.parse(JSON.parse(localStorage.getItem(real_name))[1]);
load(conteiners);
};
new_cubes=function()
{
var conteiners=document.querySelectorAll('.conteiner');
conteiners.forEach(function(conteiner){
create_el(conteiner);
});
};
restart=function()
{
var conteiners=document.querySelectorAll('.conteiner');
conteiners.forEach(function(conteiner){
delete_conteiner(conteiner);
});
new_line();
};
change_all=function()
{
var value=document.querySelector('.buttons > .to_all > textarea').value;
document.querySelector('.buttons > .to_all > textarea').value='';
var textboxes=document.querySelectorAll('.textbox');
textboxes.forEach(function(textbox){
textbox.value=value;
});
};
set_size=function()
{
//textareas in menu prepare for calculations
var textareas=document.querySelectorAll('label textarea');
textareas.forEach(function(textarea){
textarea.style.height='0px';
});
//inputs in menu prepare for calculations
var inputs=document.querySelectorAll('label input');
inputs.forEach(function(input){
input.style.height='0px';
});
//show_hide_btn prepare for calculations
var show_hide_btn=document.querySelector('.button.show_hide_menu');
show_hide_btn.style.height='0px';
//get height for textareas and inputs
var width=textareas[0].offsetWidth+'px';
var height=document.querySelector('.to_all .to_all').offsetHeight+'px';
var uls=document.querySelectorAll('label ul');
uls.forEach(function(ul){
ul.style.bottom=height;
});
//textareas set height
textareas.forEach(function(textarea){
textarea.style.height=height;
});
//inputs set height
inputs.forEach(function(input){
input.style.height=height;
input.style.width=width;
});
//show_hide_btn set height
show_hide_btn.style.height=document.querySelector('.buttons_place').offsetHeight+'px';
};
show_hide_btn_click=function(btn)
{
if(btn.style.transform==='')
{
document.querySelector('.buttons_place').style.transform='translateX('+document.querySelector('.buttons').offsetWidth+'px)'
btn.style.transform='rotateY(180deg)';
setTimeout(function()
{
btn.style.borderTopLeftRadius='0';
btn.style.borderBottomLeftRadius='0';
btn.style.borderTopRightRadius='10px';
btn.style.borderBottomRightRadius='10px';
}, 750);
}
else
{
document.querySelector('.buttons_place').style.transform='';
btn.style.transform='';
setTimeout(function()
{
btn.style.borderTopLeftRadius='';
btn.style.borderBottomLeftRadius='';
btn.style.borderTopRightRadius='';
btn.style.borderBottomRightRadius='';
}, 750);
}
};
ul_click=function(where, value)
{
where.querySelector('input').value=value;
};
create_ul=function(where, names_to_show)
{
var ul=where.querySelectorAll('ul');
if(ul!=null)
{
ul.forEach(function(ul_one_object)
{
where.removeChild(ul_one_object);
});
}
if(names_to_show.length!=0)
{
ul=document.createElement('ul');
names_to_show.forEach(function(name)
{
var li=document.createElement('li');
li.textContent=name;
ul.appendChild(li);
});
ul.addEventListener('click', function(ev){ul_click(where, ev.target.textContent);}, false);
ul.style.bottom=document.querySelector('.to_all .to_all').offsetHeight+'px';
where.appendChild(ul);
}
};
menage_ul=function(label, value)
{
var names_to_show=user_names.filter(function(name){
return name.indexOf(value)==0;
});
create_ul(label, names_to_show)
};
set_up=function()
{
var show_hide_btn=document.querySelector('.button.show_hide_menu');
show_hide_btn.addEventListener('click', function(event){show_hide_btn_click(show_hide_btn);}, false);
var width=document.body.offsetWidth;
setInterval(function()
{
if(width!=document.body.offsetWidth)
{
width=document.body.offsetWidth;
set_size();
}
}, 500);
set_size();
var add_new_line_btn=document.querySelector('.new_line');
add_new_line_btn.addEventListener('click', new_line, false);
var add_new_cubes_btn=document.querySelector('.new_cubes');
add_new_cubes_btn.addEventListener('click', new_cubes, false);
var restart_btn=document.querySelector('.restart');
restart_btn.addEventListener('click', restart, false);
var save_structure_btn=document.querySelector('.save');
save_structure_btn.addEventListener('click', save, false);
var to_all_btn=document.querySelector('.to_all .to_all');
to_all_btn.addEventListener('click', change_all, false);
var save_as_btn=document.querySelector('.save_as .save_as');
save_as_btn.addEventListener('click', save_as, false);
var open_btn=document.querySelector('.open .open');
open_btn.addEventListener('click', open, false);
var labels=document.querySelectorAll('label');
labels.forEach(function(label)
{
var input=label.querySelector('input');
if(input!=null)
{
input.addEventListener('keyup', function(ev){menage_ul(label, input.value);}, false);
}
});
on_load();
};
set_up();
})();
The removeEventListener() method removes an event handler that has been attached with the addEventListener() method.
Note: To remove event handlers, the function specified with the addEventListener() method must be an external function, like in the example below (click_event_func).
Anonymous functions, like "function (event){on_click(event, conteiner);}" will not work.
click_event_func = function(event) {
on_click(event, conteiner);
};
new_line = function() {
var conteiner = document.createElement('div');
conteiner.classList.add('conteiner');
conteiner.addEventListener('click', click_event_func, false);
document.body.appendChild(conteiner);
create_el(conteiner);
};
delete_conteiner = function(which) {
which.removeEventListener("click", click_event_func, false);
document.body.removeChild(which);
};
If you want to pass another param to an event listener you have to bind it to the function, as eventlistener will only pass the event itself (click event in your example). If you pass the exact same params and the same function when you remove it will remove it successfuly according to MDN.
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener
I think your problem is you don't use named functions.
Try to do like this:
function NameHere(event) { on_click(event, conteiner); }
new_line=function() {
var conteiner=document.createElement('div');
conteiner.classList.add('conteiner');
conteiner.addEventListener('click', NameHere, false);
document.body.appendChild(conteiner);
create_el(conteiner);
};
delete_conteiner=function(which) {
which.removeEventListener("click", NameHere, false);//this isn't working
document.body.removeChild(which);
};
The problem is when you try to remove the listener without using names, you will remove a different function from the one you added before.
By example:
which.removeEventListener("click", function (event){on_click(event, conteiner);}, false);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// This here creates a new function. It's not related to
// the function you created with addEventListener earlier.
// Trying to remove it from eventlisteners on "which" is silly
// because it doesn't exist there.
Instead:
var eventHandler = function(event){on_click(event, conteiner);}; // Store for later
conteiner.addEventListener('click', eventHandler); // Add.
conteiner.removeEventListener('click', eventHandler); // Remove.
You can remove eventListeners as long as you referencing the same function.
I have a external js file linked to my html and bundle.js file generated by webpack linked to html .
In index.html code is
<script src="js/main.js"></script>
<script type="text/javascript" src="bundle.min.js"></script>`
The issue is functions in main.js are called only once on page load in reactjs after running webpack and code is bundled.
main.js file code is
(function () {
'use strict';
var isMobile = {
Android: function() {
return navigator.userAgent.match(/Android/i);
},
BlackBerry: function() {
return navigator.userAgent.match(/BlackBerry/i);
},
iOS: function() {
return navigator.userAgent.match(/iPhone|iPad|iPod/i);
},
Opera: function() {
return navigator.userAgent.match(/Opera Mini/i);
},
Windows: function() {
return navigator.userAgent.match(/IEMobile/i);
},
any: function() {
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());
}
};
var mobileMenuOutsideClick = function() {
$(document).click(function (e) {
var container = $("#fh5co-offcanvas, .js-fh5co-nav-toggle");
if (!container.is(e.target) && container.has(e.target).length === 0) {
if ( $('body').hasClass('offcanvas') ) {
$('body').removeClass('offcanvas');
$('.js-fh5co-nav-toggle').removeClass('active');
}
}
});
};
var offcanvasMenu = function() {
$('#page').prepend('<div id="fh5co-offcanvas" />');
$('#page').prepend('<i></i>');
var clone1 = $('.menu-1 > ul').clone();
$('#fh5co-offcanvas').append(clone1);
var clone2 = $('.menu-2 > ul').clone();
$('#fh5co-offcanvas').append(clone2);
$('#fh5co-offcanvas .has-dropdown').addClass('offcanvas-has-dropdown');
$('#fh5co-offcanvas')
.find('li')
.removeClass('has-dropdown');
// Hover dropdown menu on mobile
$('.offcanvas-has-dropdown').mouseenter(function(){
var $this = $(this);
$this
.addClass('active')
.find('ul')
.slideDown(500, 'easeOutExpo');
}).mouseleave(function(){
var $this = $(this);
$this
.removeClass('active')
.find('ul')
.slideUp(500, 'easeOutExpo');
});
$(window).resize(function(){
if ( $('body').hasClass('offcanvas') ) {
$('body').removeClass('offcanvas');
$('.js-fh5co-nav-toggle').removeClass('active');
}
});
};
var burgerMenu = function() {
$('body').on('click', '.js-fh5co-nav-toggle', function(event){
var $this = $(this);
if ( $('body').hasClass('overflow offcanvas') ) {
$('body').removeClass('overflow offcanvas');
} else {
$('body').addClass('overflow offcanvas');
}
$this.toggleClass('active');
event.preventDefault();
});
};
var fullHeight = function() {
if ( !isMobile.any() ) {
$('.js-fullheight').css('height', $(window).height());
$(window).resize(function(){
$('.js-fullheight').css('height', $(window).height());
});
}
};
var contentWayPoint = function() {
var i = 0;
$('.animate-box').waypoint( function( direction ) {
if( direction === 'down' && !$(this.element).hasClass('animated-fast') ) {
i++;
$(this.element).addClass('item-animate');
setTimeout(function(){
$('body .animate-box.item-animate').each(function(k){
var el = $(this);
setTimeout( function () {
var effect = el.data('animate-effect');
if ( effect === 'fadeIn') {
el.addClass('fadeIn animated-fast');
} else if ( effect === 'fadeInLeft') {
el.addClass('fadeInLeft animated-fast');
} else if ( effect === 'fadeInRight') {
el.addClass('fadeInRight animated-fast');
} else {
el.addClass('fadeInUp animated-fast');
}
el.removeClass('item-animate');
}, k * 200, 'easeInOutExpo' );
});
}, 100);
}
} , { offset: '85%' } );
};
var dropdown = function() {
$('.has-dropdown').mouseenter(function(){
var $this = $(this);
$this
.find('.dropdown')
.css('display', 'block')
.addClass('animated-fast fadeInUpMenu');
}).mouseleave(function(){
var $this = $(this);
$this
.find('.dropdown')
.css('display', 'none')
.removeClass('animated-fast fadeInUpMenu');
});
};
var goToTop = function() {
$('.js-gotop').on('click', function(event){
event.preventDefault();
$('html, body').animate({
scrollTop: $('html').offset().top
}, 500, 'easeInOutExpo');
return false;
});
$(window).scroll(function(){
var $win = $(window);
if ($win.scrollTop() > 200) {
$('.js-top').addClass('active');
} else {
$('.js-top').removeClass('active');
}
});
};
// Loading page
var loaderPage = function() {
$(".fh5co-loader").fadeOut("slow");
};
var counter = function() {
$('.js-counter').countTo({
formatter: function (value, options) {
return value.toFixed(options.decimals);
},
});
};
var counterWayPoint = function() {
if ($('#fh5co-counter').length > 0 ) {
$('#fh5co-counter').waypoint( function( direction ) {
if( direction === 'down' && !$(this.element).hasClass('animated') ) {
setTimeout( counter , 400);
$(this.element).addClass('animated');
}
} , { offset: '90%' } );
}
};
var parallax = function() {
if ( !isMobile.any() ) {
$(window).stellar({
horizontalScrolling: false,
hideDistantElements: false,
responsive: true
});
}
};
var testimonialCarousel = function(){
var owl = $('.owl-carousel-fullwidth');
owl.owlCarousel({
items: 1,
loop: true,
margin: 0,
nav: false,
dots: true,
smartSpeed: 800,
autoHeight: true
});
};
var sliderMain = function() {
$('#fh5co-hero .flexslider').flexslider({
animation: "fade",
slideshowSpeed: 5000,
directionNav: true,
start: function(){
setTimeout(function(){
$('.slider-text').removeClass('animated fadeInUp');
$('.flex-active-slide').find('.slider-text').addClass('animated fadeInUp');
}, 500);
},
before: function(){
setTimeout(function(){
$('.slider-text').removeClass('animated fadeInUp');
$('.flex-active-slide').find('.slider-text').addClass('animated fadeInUp');
}, 500);
}
});
$('#fh5co-hero .flexslider .slides > li').css('height', $(window).height());
$(window).resize(function(){
$('#fh5co-hero .flexslider .slides > li').css('height', $(window).height());
});
};
$(function(){
mobileMenuOutsideClick();
offcanvasMenu();
burgerMenu();
contentWayPoint();
sliderMain();
dropdown();
goToTop();
loaderPage();
counterWayPoint();
counter();
parallax();
testimonialCarousel();
fullHeight();
});
}());
I want my code in main.js called always not only on reload. I am using reactjs and webpack.
Please help me fix this. Thank you.
Please note that you your code in main.js is wrapped in something like this:
(function () {
...
}());
Above construction is called Immediately-Invoked Function Expression (IIFE) - it makes code inside to be called immediately and because everything defined inside any function is visible only in this function (is not visible outside function scope) it also helps to not pollute global context. You can read more about it here.
If you want to have possibility to call it anytime you need you should use normal function (not IIFE):
function mainFunction() {
//your codde
}
and then you can call it anytime you need (also on page load).
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);
});
I need to refactor this to avoid code repetition.
$('#showmore-towns').toggle(
function() {
$('.popularTownsAdditional').show();
console.log(this);
$('#showmore-town .showless').show();
$('#showmore-town .showmore').hide();
$('#showmore-town').removeClass('sd-dark28').addClass('sd-dark28down');
return false;
},
function() {
$('.popularTownsAdditional').hide();
$('.showless').hide();
$('.showmore').show();
$('#showmore-towns').addClass('sd-dark28').removeClass('sd-dark28down');
});
$('#showmore-cities').toggle(
function() {
$('.popularCitiesAdditional').show();
$('#showmore-cities .showless').show();
$('#showmore-cities .showmore').hide();
$('#showmore-cities').removeClass('sd-dark28').addClass('sd-dark28down');
return false;
},
function() {
$('.popularCitiesAdditional').hide();
$('#showmore-cities .showless').hide();
$('#showmore-cities .showmore').show();
$('#showmore-cities').addClass('sd-dark28').removeClass('sd-dark28down');
});
basically, it shows the same functionality but only on different divs with different IDs.
Probably just need to reference a named function or two instead of the anon ones.
function showStuff(typeToShow) {
$('.popular' + typeToShow + 'Additional').show();
$('#showmore-' + typeToShow + .showless').show();
$('#showmore-' + typeToShow + .showmore').hide();
$('#showmore-' + typeToShow).removeClass('sd-dark28').addClass('sd-dark28down');
return false;
}
function hideStuff(typeToHide) {
$('.popular' + typeToHide + 'Additional').hide();
$('#showmore-' + typeToHide + .showless').hide();
$('#showmore-' + typeToHide + .showmore').show();
$('#showmore-' + typeToHide ).addClass('sd-dark28').removeClass('sd-dark28down');
}
NOTE: a) You could probably make these methods a bit slicker, but you get the idea!
NOTE: b) You'd need to rename '#showmore-town' to '#showmore-towns' (with an S) if you want to use the substitution suggested.
Then in your toggle you could reference these functions:
$('#showmore-towns').toggle(showStuff(towns),
hideStuff(towns));
$('#showmore-cities').toggle(showStuff(cities),
hideStuff(cities));
I mean...if it's always going to start with #showmore-...we can work it down
$('[id^=showmore-]').toggle(
function() {
var id = $(this).prop('id');
id = id.split('-')[1];
var upperID = id.charAt(0).toUpperCase() + id.slice(1);
$('.popular'+upperID+'Additional').show();
$('#showmore-'+id+' .showless').show();
$('#showmore-'+id+'.showmore').hide();
$('#showmore-'+id).removeClass('sd-dark28').addClass('sd-dark28down');
return false;
},
function() {
var id = $(this).prop('id');
id = id.split('-')[1];
var upperID = id.charAt(0).toUpperCase() + id.slice(1);
$('.popular'+upperID+'Additional').hide();
$('#showmore-'+id+' .showless').hide();
$('#showmore-'+id+' .showmore').show();
$('#showmore-'+id).addClass('sd-dark28').removeClass('sd-dark28down');
});
you could do:
(function() {
$('#showmore-towns').toggle(
function() { showmorelessOn('#showmore-town'); },
function() { showmorelessOff('#showmore-town'); }
);
$('#showmore-cities').toggle(
function() { showmorelessOn('#showmore-town'); },
function() { showmorelessOff('#showmore-town'); }
);
var showmorelessOn = function(context) {
$('.popularCitiesAdditional').show();
$('.showless', context).show();
$('.showmore', context).hide();
$(context).removeClass('sd-dark28').addClass('sd-dark28down');
return false;
};
var showmorelessOff = function(context) {
$('.popularCitiesAdditional').hide();
$('.showless', context).hide();
$('.showmore', context).show();
$(context).addClass('sd-dark28').removeClass('sd-dark28down');
};
})();
though I agree, could perhaps be better served on codereview.stackexchange.com
I would do this almost entirely in the CSS. Only use .toggleClass() and determine what is shown and what is hidden in the CSS.
(function() {
$('#showmore-towns').toggle(
function() { showmorelessOn.call($('#showmore-town')); },
function() { showmorelessOff.call($('#showmore-town')); }
);
$('#showmore-cities').toggle(
function() { showmorelessOn.call($('#showmore-town')); },
function() { showmorelessOff.call($('#showmore-town')); }
);
var showmorelessOn = function() {
$('.popularCitiesAdditional').show();
$('.showless', this).show();
$('.showmore', this).hide();
$(this).removeClass('sd-dark28').addClass('sd-dark28down');
return false;
};
var showmorelessOff = function() {
$('.popularCitiesAdditional').hide();
$('.showless', this).hide();
$('.showmore', this).show();
$(this).addClass('sd-dark28').removeClass('sd-dark28down');
};
})();
(function() {
$('#showmore-towns').toggle(
function() { showmoreless(); }
);
$('#showmore-cities').toggle(
function() { showmoreless(); }
);
var showmoreless = function() {
if(this.hasClass('sd-dark28'){
$('.popularCitiesAdditional').show();
$('.showless', this).show();
$('.showmore', this).hide();
$(this).removeClass('sd-dark28').addClass('sd-dark28down');
}
else
{
$('.popularCitiesAdditional').hide();
$('.showless', this).hide();
$('.showmore', this).show();
$(this).addClass('sd-dark28').removeClass('sd-dark28down');
}
}.bind($('#showmore-town'));
})();
how can i call stopEvent within init?
testObj.prototype =
{
init: function(wrapper)
{
wrapper.onclick = function(e){
stopEvent(e); //getting error stopEvent not defined
};
},
stopEvent: function(e){
if(e.preventDefault)
e.preventDefault();
else
e.returnValue = false
}
}
You could capture this:
init: function(wrapper) {
var _this = this;
wrapper.onclick = function(e) {
_this.stopEvent(e);
};
}