jQuery hamburger menu - multiple events called - javascript

$( document ).on( "vclick", '#icon', function() {
$("#navigation").animate({height: 'toggle'}, 600, "linear");
});
Hello,
I currently have a hamburger menu that works perfectly except for one thing. If I click the #icon 10 times, it will execute the code 10 times, I wonder if its possible to make it only clickable when its finished and remove the whole queue system.
Thanks in advance.

You could probably do something in the lines of this:
JavaScript
var animating = false;
$(document).on( "vclick", '#icon', function() {
if(!animating) {
animating = true;
$("#navigation").animate({height: 'toggle'}, 600, "linear", function() {
animating = false;
});
}
});

You could use a workaround by hidding the #icon until the animation done then show it again :
$( document ).on( "vclick", '#icon', function() {
var _this = $(this);
//Hide the icon
$(this).hide();
$("#navigation").animate({height: 'toggle'}, 600, "linear", function() {
//Show the icon after animation
_this.show();
});
});
Hope this helps.

Using Arg0n's answer as inspiration, I have created a jQuery plugin.
The plugin used closure which means that the function returns another function so that variables are scoped inside, rather than outside; in the global scope.
(function($) {
$.fn.collapser = function(targetSelector, ms, type) {
this.on('click', function() {
var animating = false;
return (function() {
if (!animating) {
animating = true;
$(targetSelector).animate({
height: 'toggle'
}, ms, type, function() {
animating = false;
});
}
}());
});
}
}(jQuery))
$('#icon').collapser('#navigation', 600, 'linear');
body {
background: #222;
}
#page {
background: #DDD;
}
#icon {
width: 4em;
height: 4em;
line-height: 4em;
text-align: center;
margin-bottom: 1em;
background: #446;
color: #FFF;
font-weight: bold;
cursor: pointer;
border: thin solid #AAD;
}
ul#navigation {
list-style-type: none;
margin: 0;
padding: 0.25em;
background: #E7E7F7;
}
ul#navigation li {
display: inline-block;
width: 6em;
height: 2.5em;
line-height: 2.5em;
margin: 0;
padding: 0;
text-align: center;
background: #F7F7FF;
}
#content {
background: #FFF;
padding: 1em;
height: 50vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="page">
<div id="icon">Icon</div>
<ul id="navigation">
<li>Home</li>
<li>Articles</li>
<li>Historic</li>
<li>About</li>
<li>Contact</li>
</ul>
<div id="content">
Hello World
</div>
</div>
You could also unbind and re-bind the event. This way you don't have to create any state variables. This may be the best way to handle this.
(function($) {
$.fn.collapser = function(targetSelector, ms, type) {
var eventName = 'click';
var fn = function(source) {
$(source).unbind(eventName, fn);
$(targetSelector).animate({
height: 'toggle'
}, ms, type, function() {
$(source).bind(eventName, fn);
});
};
this.on(eventName, function() {
fn.call(this);
});
}
}(jQuery))
$('#icon').collapser('#navigation', 1600, 'linear');
body {
background: #222;
}
#page {
background: #DDD;
}
#icon {
width: 4em;
height: 4em;
line-height: 4em;
text-align: center;
margin-bottom: 1em;
background: #446;
color: #FFF;
font-weight: bold;
cursor: pointer;
border: thin solid #AAD;
}
ul#navigation {
list-style-type: none;
margin: 0;
padding: 0.25em;
background: #E7E7F7;
}
ul#navigation li {
display: inline-block;
width: 6em;
height: 2.5em;
line-height: 2.5em;
margin: 0;
padding: 0;
text-align: center;
background: #F7F7FF;
}
#content {
background: #FFF;
padding: 1em;
height: 50vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="page">
<div id="icon">Icon</div>
<ul id="navigation">
<li>Home</li>
<li>Articles</li>
<li>Historic</li>
<li>About</li>
<li>Contact</li>
</ul>
<div id="content">
Hello World
</div>
</div>

Related

Changing background color doesn't work well

I am trying to change the background color of my webpage using a hamburger menu. There are 4 color options on my hamburger menu. I managed to get a change on the background when clicking on the color of my choice. But that change only happens once. When I clicked another color and come back to click that same color from earlier on, it doesn't respond. The color seems to change only once, and not more than that.
The repository to my GitHub code is here: https://github.com/tand100b/Winc_Academy
const changeColorButton1 = document.getElementById("color1");
changeColorButton1.addEventListener("click", function() {
changeClassRedBackground();
});
const changeColorButton2 = document.getElementById("color2");
changeColorButton2.addEventListener("click", function() {
changeClassOrangeBackground();
});
const changeColorButton3 = document.getElementById("color3");
changeColorButton3.addEventListener("click", function() {
changeClassPurpleBackground();
});
const changeColorButton4 = document.getElementById("color4");
changeColorButton4.addEventListener("click", function() {
changeClassGreenBackground();
});
const changeClassRedBackground = function() {
const bodyElement = document.body;
bodyElement.classList.add("red-background");
}
const changeClassOrangeBackground = function() {
const bodyElement = document.body;
bodyElement.classList.add("orange-background");
};
const changeClassPurpleBackground = function() {
const bodyElement = document.body;
bodyElement.classList.add("purple-background");
};
const changeClassGreenBackground = function() {
const bodyElement = document.body;
bodyElement.classList.add("green-background");
};
body {
background-color: pink;
}
.red-background {
background-color: red;
}
.orange-background {
background-color: orange;
}
.purple-background {
background-color: purple;
}
.green-background {
background-color: green;
}
.btn-toggle-nav {
width: 60px;
height: 20%;
background-color: #f98f39;
background-image: url("https://i.stack.imgur.com/tniUv.png");
background-repeat: no-repeat;
background-size: 60%;
background-position: center;
cursor: pointer;
}
.btn-toggle-nav:hover {
opacity: 0.5;
}
.navbar ul {
padding-top: 15px;
/* visibility: hidden; */
}
.navbar ul li {
line-height: 60px;
list-style: none;
background-color: white;
width: 300px;
padding-top: 0px;
padding-top: 0px;
}
.navbar ul li a {
display: block;
height: 60px;
padding: 0 10px;
text-decoration: none;
font-family: arial;
font-size: 16px;
}
.navbar {
background-color: red;
width: 50px;
padding: 0 5px;
height: calc(100vh-60px);
z-index: 1000;
}
.list {
margin-top: 0px;
}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<div class="btn-toggle-nav"></div>
<aside class="navbar">
<ul class="list">
<li><a id="color1" href="#">Red</a></li>
<li><a id="color2" href="#">Orange</a></li>
<li><a id="color3" href="#">Purple</a></li>
<li><a id="color4" href="#">Green</a></li>
</ul>
</aside>
Can someone please look at my code and suggest what I can do?
Firstly, I suggest declaring variable bodyElement only once at the top of the file because each time it would be the same body element:
const bodyElement = document.body
Next, we create function that will check if body has any style classes in its classList. If it's not empty (length more than one) we remove all classes, and do nothing otherwise:
const isBodyHasStyle = () => bodyElement.classList.length ? bodyElement.classList = '' : null
Then, on each button click we going to call our new function:
changeColorButton1.addEventListener("click", function() {
isBodyHasStyle()
changeClassRedBackground();
});
Do the same with other buttons.
You need to make sure that you don't have multiple classes conflicting with each other, also try to DRY your code, here is an alternative I came up with:
const changeColor = (e) => {
const bodyElement = document.querySelector('body');
const colorIdMap = {
"color1": "red-background",
"color2": "orange-background",
"color3": "purple-background",
"color4": "green-background"
}
bodyElement.className = colorIdMap[e.target.id];
}
const btns = document.querySelectorAll('.list li');
btns.forEach(btn => btn.addEventListener('click', changeColor))
An easier way to accomplish this is by setting a data attribute on the background, based on the selected color. Just add a color data value to each of the menu items and set the body background to that color.
const toggleMenu = (event) => {
event.target.closest('.navbar').classList.toggle('open');
};
const changeColor = (event) => {
document.body.dataset.background = event.target.dataset.color;
toggleMenu(event); /* do not call ~ to stay open */
};
document.querySelector('.btn-toggle-nav')
.addEventListener('click', toggleMenu)
document.querySelectorAll('.color-picker')
.forEach(e => e.addEventListener('click', changeColor));
/* CSS reset */
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
body { display: flex; background-color: #222; }
ul { list-style-type: none; margin: 0; }
body[data-background="red"] { background-color: red }
body[data-background="orange"] { background-color: orange }
body[data-background="purple"] { background-color: purple }
body[data-background="green"] { background-color: green }
body[data-background="default"] { /* Do not set the background-color */ }
a[data-color="red"] { color: red; }
a[data-color="orange"] { color: orange; }
a[data-color="purple"] { color: purple; }
a[data-color="green"] { color: green; }
a[data-color="default"] { color: #222; }
.list {
position: absolute;
display: none; /* hide menu (default) */
flex-direction: column;
gap: 0.5em;
padding: 0.5em;
width: 6em;
left: 2em;
background: #444;
}
.list li a { text-decoration: none; font-weight: bold; }
.list li a:hover { text-decoration: underline; }
.navbar {
position: relative;
width: 2em;
background-color: #444;
}
.navbar.open .list {
display: flex; /* show menu */
}
.btn-toggle-nav {
width: 2em;
height: 2em;
background-image: url("https://i.stack.imgur.com/tniUv.png");
background-repeat: no-repeat;
background-size: 60%;
background-position: center;
cursor: pointer;
}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<aside class="navbar">
<div class="btn-toggle-nav"></div>
<ul class="list">
<li><a class="color-picker" href="#" data-color="red">Red</a></li>
<li><a class="color-picker" href="#" data-color="orange">Orange</a></li>
<li><a class="color-picker" href="#" data-color="purple">Purple</a></li>
<li><a class="color-picker" href="#" data-color="green">Green</a></li>
<li><a class="color-picker" href="#" data-color="default">Default</a></li>
</ul>
</aside>

connecting two divs using jsplumb

I am using jsplumb to my project. I mentioned my sample below. when I drag and drop divs, divs should appear with anchor points. anchors points can be connecting each other. in my sample I did drag and drop part. but I am unable to complete the rest of the work. please check my sample and get an idea.
I am using jsplumb to my project. I mentioned my sample below. when I drag and drop divs, divs should appear with anchor points. anchors points can be connecting each other. in my sample I did drag and drop part. but I am unable to complete rest of the work. please check my sample and get the idea.
jsPlumb.ready(function() {
jsPlumb.Defaults.Container=$("#dropArea");
jsPlumb.Defaults.PaintStyle = { strokeStyle:"palevioletred", lineWidth:2,
dashstyle: '3 3'};
jsPlumb.Defaults.EndpointStyle = { radius:7, fillStyle:"palevioletred" };
jsPlumb.importDefaults({Connector : [ "Bezier", { curviness:50 } ]});
jsPlumb.connect({
connector: ["Straight"],
source:"element",
target:"element",
anchor: ["Left", "Right"],
endpoint:"Dot"
});
jsPlumb.setContainer($('#dropArea'));
var i = 1;
$(".element").draggable ({
helper : 'clone',
cursor : 'pointer',
tolerance : 'fit',
revert : true
});
$("#dropArea").droppable ({
accept: '.element',
containment: 'dropArea',
drop: function (e, ui) {
droppedElement = ui.helper.clone();
ui.helper.remove();
$(droppedElement).removeAttr("class");
jsPlumb.repaint(ui.helper);
var newAgent = $('<div>').attr('id', 'pro' + i).addClass('pro');
newAgent.text('Element ' + i);
$(droppedElement).draggable({containment: "dropArea"});
$('#dropArea').append(newAgent);
jsPlumb.draggable(newAgent, {
containment: 'parent' });
i++;
}
});
$("#dropArea").on('click', '.pro', function (e) {
i++;
var newState = $('<div>').attr('id', 'state' + i).addClass('section').
text('Section '+ (i-1));
var title = $('<div>').addClass('title');
var connect = $('<div>').addClass('connector').
text('Click here to drag');
newState.css({
'top': e.pageY,
'left': e.pageX });
newState.append(title);
newState.append(connector);
$(this).append(newState);
jsPlumb.makeTarget(newState, {
anchor: 'Continuous' });
jsPlumb.makeSource(connector, {
anchor: 'Continuous' });
newState.dblclick(function (e) {
jsPlumb.detachAllConnections($(this));
$(this).remove();
e.stopPropagation();
});
});
});
#import url(http://fonts.googleapis.com/css?family=Montserrat);
* {
font-family: 'Montserrat', sans-serif;
}
#dropArea {
position: relative;
resize: both;
margin-left: 180px;
border: 1px solid #aaaaaa;
width: 800px;
height: 650px;
overflow-x: scroll;
overflow-y: scroll;
}
.title {
padding: 10px;
cursor: move;
}
.connector {
font-size:10px;
text-align: center;
width: 100%;
height: 20px;
background-color: #ffffff;
cursor: pointer;
}
.element {
border: 1px solid gray;
text-align: center;
width: 170px;
height: 75px;
background-color: lightpink;
position: absolute;
}
.pro {
border: 1px solid gray;
text-align: center;
width: 170px;
height: 75px;
background-color: lightpink;
position: absolute;
}
.section {
font-size: 12px;
text-align: center;
font-weight: 200;
border: 1px solid black;
background-color: #ddddff;
}
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsPlumb/2.7.13/js/jsplumb.js"></script>
</head>
<body>
<div class="element" id="cId">
</div>
<div id="dropArea">
</div>
</body>
</html>

Onclick Console Message Not Firing (javascript)

I'm building a simple flexbox navigation, when the screen size is below 980px a mobile version of the menu shows.
I'm building the javascript to do this - does anyone know why my console.log message won't show when the mobile version of the menu is clicked?
It's driving me around the twist.
You have to reduce the window below 980px to see what i mean.
https://codepen.io/emilychews/pen/eezYox?editors=1111
var mobileMenuButton = document.querySelector('#mobile-menu')
var mobileMenuItems = document.querySelector('#nav-menu')
// TOGGLE MOBILE MENU
var mobileMenu = false
if (mobileMenu === false) {
function showMobileMenu() {
mobileMenuButton.onclick(function() {
console.log("clicked");
})
mobileMenu = true
}
}
* {
padding: 0;
margin: 0;
}
p {
color: white;
}
header {
width: 100%;
}
#main-nav {
width: inherit;
height: 100px;
background: red;
display: flex;
justify-content: space-between;
box-sizing: border-box;
padding: 10px 5% 10px 5%;
align-items: center;
}
#logo-wrapper {
width: 150px;
height: 75px;
background-color: grey;
}
#nav-menu {
display: flex;
margin-left: auto;
}
#main-nav ul li {
list-style-type: none;
padding: 5px 10px;
background: pink;
margin: 0 5px;
}
#mobile-menu {
display: none;
}
#media only screen and (max-width: 980px) {
#mobile-menu {
display: block;
background: blue;
padding: 5px 10px;
box-sizing: border-box;
cursor: pointer;
z-index: 99;
}
#main-nav ul {
display: none;
}
}
<header>
<nav id="main-nav">
<div id="logo-wrapper"></div>
<ul id="nav-menu">
<li class="menu-item menu-item-1"><a>Home</a></li>
<li class="menu-item menu-item-2"><a>About</a></li>
<li class="menu-item menu-item-3"><a>Contact</a></li>
</ul>
<div id="mobile-menu"><a>Menu</a></div>
</nav>
</header>
Well there are few problems, You never call showMobileMenufunction, so event is never binded to that element. onclick is not a function. I believe You want something like:
var mobileMenuButton = document.querySelector('#mobile-menu')
var mobileMenuItems = document.querySelector('#nav-menu')
// TOGGLE MOBILE MENU
var mobileMenu = false
mobileMenuButton.addEventListener('click', function() {
toggleMobileMenu();
})
function toggleMobileMenu() {
if (mobileMenu === false) {
console.log("clicked");
mobileMenu = true
} else {
console.log("not clicked");
mobileMenu = false
}
}

Translating jQuery to Vanilla JS issue

I was watching a tutorial that used jQuery and wanted to turn it into JS, but my code is broken - was hoping someone could help me with this:
Tutorial JS:
$(function() {
var btn = $('button');
var progressBar = $('.progressbar');
btn.click(function() {
progressBar.find('li.active').next().addClass('active');
})
})
Taken from URL:http://www.kodhus.com/kodity/codify/kod/mGXAtb
Here is my failed attempt at rewriting the jQuery using JavaScript DOM:
var btn1 = document.getElementsByTagName('BUTTON');
var progBar = document.getElementsByClassName('progressbar');
function clickMe1() {
var elm = progBar.querySelectorAll("li");
var emlClass = elm.querySelector(".active");
return emlClass.nextElementSibling.addClass('active');
}
btn1.addEventListener("click", clickMe1, false);
where did I go wrong?
Working fiddle.
Your code will work after several changes check the notes below :
You've missed addClass() there it's a jQuery function, for vanilla JS use .classList.add() instead:
return emlClass.nextElementSibling.classList.add("active");
querySelectorAll(); will return a list of nodes you have to loop through them and add class, use :
var emlClass = progBar.querySelectorAll("li.active");
Instead of :
var elm = progBar.querySelectorAll("li");
var emlClass = elm.querySelector(".active");
Then loop and add active class:
for(var i=0;i<emlClass.length;i++){
emlClass[i].nextElementSibling.classList.add("active");
}
getElementsByTagName() and getElementsByClassName() will also returns a list of nodes with given name, you have to specify which one you want to pick (selecting the first in my example) :
var btn1 = document.getElementsByTagName('BUTTON')[0];
var progBar = document.getElementsByClassName('progressbar')[0];
Hope this helps.
var btn1 = document.getElementsByTagName('BUTTON')[0];
var progBar = document.getElementsByClassName('progressbar')[0];
function clickMe1() {
var emlClass = progBar.querySelectorAll("li.active");
for(var i=0;i<emlClass.length;i++){
emlClass[i].nextElementSibling.classList.add("active");
}
}
btn1.addEventListener("click", clickMe1, false);
.container {
width: 100%;
}
.progressbar {
counter-reset: step;
margin: 0;
margin-top: 50px;
padding: 0;
}
.progressbar li {
list-style-type: none;
float: left;
width: 33.33%;
position: relative;
text-align: center;
}
.progressbar li:before {
content: counter(step);
counter-increment: step;
width: 30px;
height: 30px;
line-height: 30px;
border: 2px solid #ddd;
display: block;
text-align: center;
margin: 0 auto 10px auto;
border-radius: 50%;
background-color: white;
}
.progressbar li:after {
content: '';
position: absolute;
width: 100%;
height: 2px;
background-color: #ddd;
top: 15px;
left: -50%;
z-index: -1;
}
.progressbar li:first-child:after {
content: none;
}
.progressbar li.active {
color: green;
}
.progressbar li.active:before {
border-color: green;
}
.progressbar li.active + li:after {
background-color: green;
}
button {
position: relative;
border: none;
padding: 10px 20px;
font-size: 16px;
border-radius: 2px;
left: 50%;
margin-top: 30px;
transform: translate(-50%);
cursor: pointer;
outline: none;
}
button:hover {
opacity: 0.8;
}
<div class="container">
<ul class="progressbar">
<li class="active">Step 1</li>
<li>Step 2</li>
<li>Step 3</li>
</ul>
</div>
<button>Next step</button>
.querySelectorAll("li") will return an array (or an array-like object) with one or more <li> tags. So you need to either:
loop through every <li> in that list and do the rest,
or just take the first item from that list if you don't want to worry about there being more than one li in the page,
or use .querySelector (not .querySelectorAll) to just take the first <li> for you.
MDN

Optimize jQuery code

I've written this jQuery code that fades in a overlay with some links over an image. What i found out is that it is painfully slow when I add like 10 of these images. I would really appreciate some tips and tricks on how to make this code faster.
If you have some tips for my HTML and CSS that would be great too ;)
jQuery code
$(document).ready(function() {
var div = $(".thumb").find("div");
div.fadeTo(0, 0);
div.css("display","block");
$(".thumb").hover(
function () {
$(this).children(".download").fadeTo("fast", 1);
$(this).children(".hud").fadeTo("fast", 0.7);
},
function () {
div.fadeTo("fast", 0);
}
);
});
All the code
<style type="text/css">
a:active {
outline:none;
}
:focus {
-moz-outline-style:none;
}
img {
border: none;
}
#backgrounds {
font: 82.5% "Lucida Grande", Lucida, Verdana, sans-serif;
margin: 50px 0 0 0;
padding: 0;
width: 585px;
}
.thumb {
margin: 5px;
position: relative;
float: left;
}
.thumb img {
background: #fff;
border: solid 1px #ccc;
padding: 4px;
}
.thumb div {
display: none;
}
.thumb .download {
color: #fff;
position: absolute;
top: 0;
left: 0;
z-index: 999;
padding: 0 10px;
}
.thumb .download h3 {
font-size: 14px;
margin-bottom: 10px;
margin-top: 13px;
text-align: center;
}
.thumb .download a {
font-size: 11px;
color: #fff;
text-decoration: none;
font-weight: bold;
line-height: 16px;
}
.thumb .download a:hover {
text-decoration: underline;
}
.thumb .download .left, .thumb .download .right {
width: 44%;
margin: 0;
padding: 4px;
}
.thumb .download .left {
float: left;
text-align: right;
}
.thumb .download .right {
float: right;
text-align: left;
}
.thumb img, .thumb .hud {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
.thumb .hud {
width: 100%;
height: 110px;
position: absolute;
top: 0;
left: 0;
background: #000;
}
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var div = $(".thumb").find("div");
div.fadeTo(0, 0);
div.css("display","block");
$(".thumb").hover(
function () {
$(this).children(".download").fadeTo("fast", 1);
$(this).children(".hud").fadeTo("fast", 0.7);
},
function () {
div.fadeTo("fast", 0);
}
);
});
</script>
<div id="backgrounds">
<div class="thumb">
<div class="download">
<h3>Download wallpaper</h3>
<p class="left">
1024x768
1280x800
1280x1024
</p>
<p class="right">
1440x900
1680x1050
1920x1200
</p>
</div>
<div class="hud"></div>
<img alt="image" src="thumb.jpg"/>
</div>
</div>
I got it to respond a little better by simply changing the following within the hover(..):
function () {
$(".download", this).fadeTo("fast", 1);
$(".hud", this).fadeTo("fast", 0.7);
},
function () {
$(".download, .hud", this).fadeTo("fast", 0);
}
The biggest difference comes from only applying the hoverout effect to the event target, no need to reapply to all your divs on the page.
I've put your code into a test page and to be perfectly honest, even with thirty or so .thumb divs it seemed ok - certainly responsive enough to use from my end. Sliding the mouse over a bunch of them means I have to wait for the rollover effect to go through them all which takes a while until it gets to the one I've actually stopped on, but surely that was what you wanted given that you're using 'hover' rather than 'click' (which would certainly remove any speed issues).
I'm not using actual images in my test page, just getting the alt text, so my best current guess would be to make sure all images you're loading are as small filesize as you can possibly make them.
Pre-Select MORE
Good job preselecting the div. Try this way so that it pre-selects the fade in elements as well instead of doing it on hover:
$().ready(function() {
var div = $(".thumb").find("div");
div.fadeTo(0, 0);
div.css("display","block");
$(".thumb").each(function() {
var download = $(this).children(".download");
var hud = $(this).children(".hud");
$(this).hover(
function () {
download.fadeTo("fast", 1);
hud.fadeTo("fast", 0.7);
},
function () {
div.fadeTo("fast", 0);
}
);
});
});
try removing the
:focus {
-moz-outline-style:none;
}
and see what happens

Categories