Function to make the code smaller - javascript

Im trying to learn javascript mostly by trial and error, i have created a dropdown menu which probably has ALOT of unnessecary code in it.. How do i create this the right way? any pointer would be appreciated!
JS:
function dropdown() {
var dropdownTrigger = document.getElementById("dd");
var dropdownTrigger2 = document.getElementById("dd2");
var dropdownTrigger3 = document.getElementById("dd3");
var dropdownTrigger4 = document.getElementById("dd4");
if (dropdownTrigger.style.display == "none") {
dropdownTrigger.style.display="block";
dropdownTrigger2.style.display="none";
dropdownTrigger3.style.display="none";
dropdownTrigger4.style.display="none";
}
else {
dropdownTrigger.style.display="none";
}
}
function dropdown2() {
var dropdownTrigger = document.getElementById("dd");
var dropdownTrigger2 = document.getElementById("dd2");
var dropdownTrigger3 = document.getElementById("dd3");
var dropdownTrigger4 = document.getElementById("dd4");
if (dropdownTrigger2.style.display == "none") {
dropdownTrigger.style.display="none";
dropdownTrigger2.style.display="block";
dropdownTrigger3.style.display="none";
dropdownTrigger4.style.display="none";
}
else {
dropdownTrigger2.style.display="none";
}
}
function dropdown3() {
var dropdownTrigger = document.getElementById("dd");
var dropdownTrigger2 = document.getElementById("dd2");
var dropdownTrigger3 = document.getElementById("dd3");
var dropdownTrigger4 = document.getElementById("dd4");
if (dropdownTrigger3.style.display == "none") {
dropdownTrigger.style.display="none";
dropdownTrigger2.style.display="none";
dropdownTrigger3.style.display="block";
dropdownTrigger4.style.display="none";
}
else {
dropdownTrigger3.style.display="none";
}
}
function dropdown4() {
var dropdownTrigger = document.getElementById("dd");
var dropdownTrigger2 = document.getElementById("dd2");
var dropdownTrigger3 = document.getElementById("dd3");
var dropdownTrigger4 = document.getElementById("dd4");
if (dropdownTrigger4.style.display == "none") {
dropdownTrigger.style.display="none";
dropdownTrigger2.style.display="none";
dropdownTrigger3.style.display="none";
dropdownTrigger4.style.display="block";
}
else {
dropdownTrigger4.style.display="none";
}
}
function dropdownAll() {
var ddaText = document.getElementById("dda");
var dropdownTrigger1 = document.getElementById("dd");
var dropdownTrigger2 = document.getElementById("dd2");
var dropdownTrigger3 = document.getElementById("dd3");
var dropdownTrigger4 = document.getElementById("dd4");
if (ddaText.innerHTML == "Open all"){
ddaText.innerHTML = "Hide all";
dropdownTrigger1.style.display="block";
dropdownTrigger2.style.display="block";
dropdownTrigger3.style.display="block";
dropdownTrigger4.style.display="block";
}
else {
ddaText.innerHTML = "Open all";
dropdownTrigger1.style.display="none";
dropdownTrigger2.style.display="none";
dropdownTrigger3.style.display="none";
dropdownTrigger4.style.display="none";
}
}
HTML:
<div class="left-menu">
<p>Menu</p>
<br>
+Menu 1
<ul id="dd" style="display:none;">
<li>item 1:1</li>
<li>item 1:2</li>
<li>item 1:3</li>
<li>item 1:4</li>
</ul>
+Menu 2
<ul id="dd2" class="dropdown" style="display:none;">
<li>item 2:1</li>
<li>item 2:2</li>
<li>item 2:3</li>
<li>item 2:4</li>
</ul>
+Menu 3
<ul id="dd3" class="dropdown" style="display:none;">
<li>item 3:1</li>
<li>item 3:2</li>
<li>item 3:3</li>
<li>item 3:4</li>
</ul>
+Menu 4
<ul id="dd4" class="dropdown" style="display:none;">
<li>item 4:1</li>
<li>item 4:2</li>
<li>item 4:3</li>
<li>item 4:4</li>
</ul>
Open all
</div>
is it possible to create function that increases or some kind of loop for this?

Maybe consider something along the lines of this. This function uses a ternary operator to check if the display value is set to block or none, and switches it accordingly.
<button href="#" onclick="toggleDisplay(document.getElementById('dd'))"> +Menu 1</button>
<ul id="dd" style="display:none;">
<li>item 1:1</li>
<li>item 1:2</li>
<li>item 1:3</li>
<li>item 1:4</li>
</ul>
<script>
function toggleDisplay(el){
el.style.display === 'none' ? // Is it invisible?
el.style.display = 'block' : // Then use this
el.style.display = 'none' // If not the use this
}
</script>
Edit: Misread; don't use a <select> element.
And I will actually better explain what is happening here. The function toggleDisplay(el) takes an element as a parameter.
<button onclick=toggleDisplay( <your element goes here> )>
Now the element that we are passing has a style object that we can access and make changes to. You can reference that here w3school.
Edit 2: Here's another solution that attaches an event to elements in an HTML class list. The events toggle the current element's direct sibling element. You can use this with any number of different combinations that you might need it for. This way you won't need any inline JavaScript in your HTML and you won't have to insert each element individually.
Myself and most other people would recommend trying out jQuery for this kind of DOM traversing and event handling. The DOM is absolutely terrible in terms of browser compatibility and the headache involved with traversal. jQuery solves a lot of that headache.
// this returns a list of your buttons with the class name 'toggle-button'
var buttons = document.getElementsByClassName('toggle-button')
// here we iterate over them to individually modify
for (var i = 0; i <= buttons.length-1; i++){
// here we take the current button and we add an event listener to it
buttons[i].addEventListener('click', function(){
// 'this' refers to buttons[i] that we are targeting with our event
var el = this.nextElementSibling // nextElementSibling is the ul
el.style.display === 'none' ? // Is it invisible?
el.style.display = 'block' : // Then use this
el.style.display = 'none' // If not then use this
})
}
<button class="toggle-button"> +Menu 1</button>
<ul id="dd0" style="display:none;">
<li>item 1:1</li>
<li>item 1:2</li>
<li>item 1:3</li>
<li>item 1:4</li>
</ul>
<button class="toggle-button"> +Menu 1</button>
<ul id="dd1" style="display:none;">
<li>item 2:1</li>
<li>item 2:2</li>
<li>item 2:3</li>
<li>item 2:4</li>
</ul>
<button class="toggle-button"> +Menu 1</button>
<ul id="dd2" style="display:none;">
<li>item 3:1</li>
<li>item 3:2</li>
<li>item 3:3</li>
<li>item 3:4</li>
</ul>

You could use JQuery,
<a class="menu1" href="#"> +Menu 1</a>
<ul id="dd" style="display:none;">
<li>item 1:1</li>
<li>item 1:2</li>
<li>item 1:3</li>
<li>item 1:4</li>
</ul>
<a class="menu2" href="#"> +Menu 2</a>
<ul id="dd2" style="display:none;">
<li>item 2:1</li>
<li>item 2:2</li>
<li>item 2:3</li>
<li>item 2:4</li>
</ul>
And here's the JQuery code:
$(document).ready(function() {
$(".menu1").toggle(function() {
function() {$("#dd").css("display", "block");},
function() {$("#dd").css("display", "none");}
});
$(".menu2").toggle(function() {
function() {$("#dd2").css("display", "block");},
function() {$("#dd2").css("display", "none");}
});
});

A CSS only solution to your current problem
The problem your are currently trying to solve can be achieved without the use of javascript at all, and would most likely be a cleaner and more scalable solution in the end.
The theory:
We use a div which wraps the menu elements, and whenever this is hovered (the :hover property), we show the menu. We can even add a nice little animation to this if we care.
<style>
div.menuopener{
display: block;
}
ul.submenu {
/*display: none; USE THIS IN STEAD TO LOOK LIKE YOUR EXAMPLE*/
/* All of this is to make it look nice, and is not needed: */
max-height: 0;
overflow: hidden;
-webkit-transition: max-height 0.8s;
-moz-transition: max-height 0.8s;
transition: max-height 0.8s;
}
/*Here we select all elements of class menuopener when it is hovered, and then
select the sub element ul of class submenu and apply styles to it */
.menuopener:hover ul.submenu{
/*display: block; USE THIS IN STEAD TO LOOK LIKE YOUR EXAMPLE*/
/* max-height is just for the transition, and not needed*/
max-height: 200px;
}
</style>
<div class="menuopener">
<p>Menu 1</p>
<ul class="submenu" >
<li>item 1:1</li>
<li>item 1:2</li>
<li>item 1:3</li>
<li>item 1:4</li>
</ul>
</div>
<div class="menuopener">
<p>Menu 2</p>
<ul class="submenu">
<li>item 2:1</li>
<li>item 2:2</li>
<li>item 2:3</li>
<li>item 2:4</li>
</ul>
</div>
<div class="menuopener">
<p>Menu 3</p>
<ul class="submenu">
<li>item 3:1</li>
<li>item 3:2</li>
<li>item 3:3</li>
<li>item 3:4</li>
</ul>
</div>
<div class="menuopener">
<p>Menu 4</p>
<ul class="submenu">
<li>item 4:1</li>
<li>item 4:2</li>
<li>item 4:3</li>
<li>item 4:4</li>
</ul>
</div>
Hope this helps a bit. It might not be the perfect solution, but it should clean up your current situation and provide a much smaller, and easier to maintain, codebase.
I do understand that what you are after is to learn javascript, but there are other, more fun, opportunities to do this other than opening menus, like https://projecteuler.net/ or https://www.codingame.com/

Related

Items on left to be added in the box on the right upon clicked

I have a list of items placed on the left (itemDrop) and would like to add it in the box on the right (itemList) when selected. Terms: unable to select the same item, unable to select more than 3 items. Can only edit javascript file, HTML file untouchable.
HTML:
<div id="itemSelectHeader"> <H2>Item List</H2>
<div id="itemSelect">SELECT FROM HERE
<div id="itemDrop" class="itemSetup">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
</ul>
</div>
</div>
</div>
<div id="itemListSection">
<H3>Items Selected</H3>
<div id="itemList" class="itemSetup"> <ul></ul></div>
</div>
Add ons:
- clear box when clear button clicked
- submit only when there are 3 items selected
<div id="itemBtn">
<button type="button" id="clearItem">CLEAR ALL</button>
<button type="button" id="selectItem">SUBMIT</button>
</div>
tried the following for clear button but it doesn't work. But worked for alert
$("#clearItem").click(function(){
//alert("try");
$("#itemList").reset();
});
You can get lists in html by tag selector and then add an click event listener to first list for appending childs to second list.
Look at the following:
let lists = document.getElementsByTagName("ul");
lists[0].addEventListener("click",function(e) {
if(e.target && e.target.nodeName == "LI" && lists[1].childElementCount<3) {
lists[1].appendChild(e.target)
}
});
<div id="itemSelectHeader">
<H2>Item List</H2>
<div id="itemSelect">SELECT FROM HERE
<div id="itemDrop" class="itemSetup">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
</ul>
</div>
</div>
</div>
<div id="itemListSection">
<H3>Items Selected</H3>
<div id="itemList" class="itemSetup"> <ul></ul></div>
</div>
this will serve your terms.
Terms: unable to select the same item, unable to select more than 3 items. Can only edit javascript file
let counter = 0;
let selectedItem = [];
let drop = document.querySelector("#itemList ul");
let lists = document.querySelectorAll("#itemDrop ul li");
for (let i = 0; i < lists.length; i++) {
lists[i].addEventListener("click", function() {
appendPlace(i, lists[i]);
});
}
function appendPlace(itemNo, item) {
if(!selectedItem[itemNo] && counter < 3)
{
let x = document.createElement("LI");
let t = document.createTextNode(item.innerHTML);
x.appendChild(t);
drop.appendChild(x);
selectedItem[itemNo] = 1;
counter++;
}
}

JQuery mobile changing element between list

I have two lists and when clicked on one element I want to remove it from this list and add it to a second one.
<div id="ul1">
<ul id="selected">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>
<hr />
<div id="ul2">
<ul id="list2">
<li>Item 10</li>
<li>Item 20</li>
<li>Item 30</li>
<li>Item 40</li>
</ul>
</div>
I am not a jQuery dev and I can't have this working properly.
$("#ul1 a").click(function(){
var para1 = this.dataset['para1'];
$(this).closest('li').remove();
add2(para1);
return false;
});
$("#ul2 a").click(function(){
var para1 = this.dataset['para1'];
$(this).closest('li').remove();
add(para1);
return false;
});
function add(p1){
$("#selected").append('<li><a href="" data-para1='+p1+'>'+p1+'</a></li>');
return false;
}
function add2(p1){
$("#list2").append('<li><a href="" data-para1='+p1+'>'+p1+'</a></li>');
return false;
}
I would really appreciate if someone could point out the correct solution to accomplish this.
Here is a demo.
Thanks
Just use appendTo method.
$("#ul1").on('click', 'a', function(){
$(this).closest('li').appendTo('#list2');
return false;
});
$("#ul2").on('click', 'a', function(){
$(this).closest('li').appendTo('#selected');
return false;
});
Have a look at this snippet. It checks the click() event of the anchor inside the li, looks for the li by matching the father of the element.
$(document).ready(function() {
$('#select li a').each(function(index, anchor) {
// Let's add a click listener
$(anchor).click(function(){
// Let's find the father li
$(this).closest('li').appendTo('#append');
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="ul1">
<ul id="select">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
</ul>
</div>
<hr />
<div id="ul2">
<ul id="append">
<li>Item 10</li>
<li>Item 20</li>
<li>Item 30</li>
<li>Item 40</li>
</ul>
</div>
Try this way :
$("#ul1 a").click(function(){
var para1 = $(this).closest('li').html()
$(this).closest('li').remove();
add2(para1);
return false;
});
$("#ul2 a").click(function(){
var para1 = $(this).closest('li').html()
$(this).closest('li').remove();
add(para1);
return false;
});
function add(p1){
("#list2").append(p1);
return false;
}
function add2(p1){
$("#list2").append(p1);
return false;
}
try:
function move(ul1,ul2) {
$('body').on('click',ul1+" a", function(){
$(ul2).append($(this).parent('li'));
//$(this).parent('li').remove();
return false;
});
};
move('#selected','#list2');
move('#list2','#selected');
https://jsfiddle.net/x9LL1ubc/2/

JavaScript: Slide a UL down to last element

Can't think how best to do this. Thought it would be a simple show/hide but it dosn't seem as simple as that.
There is a UL with an indeterminable amount of items in it. It needs to be able to show the first 10 but no more unless a 'show more' button is clicked. When the 'show more' button is clicked it will expand the list open to show the complete list.
http://jsfiddle.net/kbUhW/
Interested to see how this is achieved.
Here is an example: http://jsfiddle.net/WqxGf/
JS:
count = 0;
$('ul li').hide();
$('ul').children().each(function(){
if(count >= 10) return;
$(this).show();
count++;
})
$('.slide').click(function(){$('ul li').show('blind');})
HTML:
<ul>
<li>Item One</li>
<li>Item Two</li>
<li>Item Three</li>
<li>Item Four</li>
<li>Item Five</li>
<li>Item Six</li>
<li>Item One</li>
<li>Item Two</li>
<li>Item Three</li>
<li>Item Four</li>
<li>Item Five</li>
<li>Item Six</li>
<li>Item One</li>
<li>Item Two</li>
<li>Item Three</li>
<li>Item Four</li>
<li>Item Five</li>
<li>Item Six</li>
<li>Item One</li>
<li>Item Two</li>
<li>Item Three</li>
<li>Item Four</li>
<li>Item Five</li>
<li>Item Six</li>
<li>Item One</li>
<li>Item Two</li>
<li>Item Three</li>
<li>Item Four</li>
<li>Item Five</li>
<li>Item Six</li>
</ul>
<a href="#" class='slide'>Slide Down</a>
All the other answers use jQuery, but your question didn't actually specify it. So here's one way to do it with plain JavaScript. Let's assume your <ul> has the ID foo, your "reveal" link has the ID reveal, and that there's a class hide with display: none. Then we have:
(function getChildNodes(id, num) { // ID of element, number to show
var obj = document.getElementById(id),
children = obj.childNodes,
elemcounter = 0;
for (var i = 0; i < children.length; i++) { // loop all children
if (children[i].nodeType === 1) { // examine elements only
elemcounter++;
if (elemcounter > num) { // element number in range to hide?
children[i].className = 'hide';
}
}
}
}('foo', 3)); // id foo, show 3
document.getElementById('reveal').onclick = function() { // handle click
var items = document.getElementsByTagName('li');
for( var i = 0; i < items.length; i++ ){ // for all list elements...
var tempclass = items[i].className;
// if the class is "hide", unhide
items[i].className = tempclass === 'hide' ? '' : tempclass;
}
}
Of course there are many other ways to do this more thoroughly -- and this one doesn't even slide. jQuery does make life a bit easier.
Here's the working example: http://jsfiddle.net/redler/jsQ47/
Here's with the slide down effect:
http://jsfiddle.net/deNzh/
That's what you're looking for, right?
you could assign the first ten < li >s a class like < li class="always_show">Stuff goes here< /li > and then make a script that hides all, shows the "always_show" class and waits for a button click to show the whole thing.
might look something like:
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$(function(){
$("#listorama").hide();
});
$(function(){
$(".always_show").show();
});
$(function(){
$("#show_all").click(function(){
$("#listorama").show();
});
});
</script>
<ul id="listorama">
<li class="always_show"></li>
<li class="always_show"></li>
<li class="always_show"></li>
<li class="always_show"></li>
<li class="always_show"></li>
<li class="always_show"></li>
<li class="always_show"></li>
<li class="always_show"></li>
<li class="always_show"></li>
<li class="always_show"></li>
<li>stuff to hide first</li>
<li>stuff to hide first</li>
<li>stuff to hide first</li>
<li>stuff to hide first</li>
<li>stuff to hide first</li>
</ul>
<button id="show_all">Show All</button>
Hope this helps!
Andy
function toggleListDisplay (list, cap) {
cap = parseInt(cap);
if (cap == null || cap < 0) { return; }
var elements = $(list).children();
if ($(elements[cap]).css('display') == 'none') {
// means we need to expand the list
elements.each(function(ind, ele) {
if (ind >= cap) { $(ele).slideDown(); }
});
$('.slide').html('Slide Up');
} else {
// means we need to shorten the list
elements.each(function(ind, ele) {
if (ind >= cap) { $(ele).slideUp(); }
});
$('.slide').html('Slide Down');
}
}
$('.slide').click(function(){
toggleListDisplay('#tester', 10);
})
toggleListDisplay('#tester', 10);
JSFiddle: http://jsfiddle.net/WqxGf/7/
I don't know why the others feel like making such a simple task more complicated than it is, but here is a much easier, shorter, and simpler way of achieving this:
$("a").click(function() {
var ul = $("#myid");
ul.animate({"height": ul[0].scrollHeight}, 1000);
});
Example: http://jsfiddle.net/kbUhW/13/

Expand list with slideToggle

I have an unordered list like this one:
Show the rest
<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
<li>Item 9</li>
<li>Item 10</li>
</ul>
and this jQuery code:
var list = $('#myList li:gt(4)');
list.hide();
$('a#myList-toggle').click(function() {
list.slideToggle(400);
return false;
});
The problem is that it slides each individual li item, i need to slide the rest of the list, like i would slide the whole list.
How can I do that?
your method didn't work because it would find the height with height: auto.
After a lot of fail and try, I came up with something that works, almost.
Do you have any comment on my code, I would really appreciate it.
And how would I do it, if I want the same link to collapse the list again
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var list = $('ul#myList');
var original_height = list.height();
list.css({height:$('#myList li').height()*5});
$('a#myList-toggle').click(function() {
list.animate({height:original_height})
return false;
});
});
</script>
<style type="text/css">
ul#myList {
overflow: hidden;
}
</style>
</head>
<body>
Show the rest
<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
<li>Item 9</li>
<li>Item 10</li>
</ul>
</body>
</html>
Pretty clumsy solution IMHO, but if it works for you - it works for you...
For the list to collapse and expand by clicking on the same link:
$(document).ready(function() {
var list = $('ul#myList');
var original_height = list.height();
var new_height = $('#myList li').height()*5;
list.css({height:new_height});
$('a#myList-toggle').click(function() {
if( list.height() == original_height ) {
list.animate({height:new_height});
} else {
list.animate({height:original_height});
}
return false;
});
});
Quick & not-so-dirty way: wrap it with a div element and slideToggle('#myList div.wrapper').
You can give a height to UL tag with overflow:hidden. Then you use animation({height:auto}) to show all. Otherwise, you don't have any viable solution.
Whats the problem with simply toggeling the list instead of the elements?
$(function(){
var listheight = $("#mylist").height();
$("a#myList-toggle").toggle(function(){
$("#mylist").slideToggle();
},function(){$("#mylist").animate({height:listheight})});
});

jQuery Expanding & Collapsing lists

The code expands and collapses a list in which list items can have sublists. Any ideas to refactor this code - especially the toggling part. Is it necessary to use closures here ?
$(function()
{
$('li:has(ul)')
.click(function(event){
if (this == event.target)
{
var that = this;
$('li:has(ul)').children().filter(':not(:hidden)').parent().each(function(x){
if(this != that)
toggleList(this);
});
toggleList(this);
}
})
.css({cursor:'pointer', 'list-style-image':'url(plus.gif)'})
.children().hide();
$('li:not(:has(ul))').css({cursor: 'default', 'list-style-image':'none'});
});
function toggleList(L)
{
$(L).css('list-style-image', (!$(L).children().is(':hidden')) ? 'url(plus.gif)' : 'url(minus.gif)');
$(L).children().toggle('fast');
}
EDIT:
The script works on the following HTML snippet (source: jQuery in Action). Actually I was trying to extend the script given in the book.
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>
Item 3
<ul>
<li>Item 3.1</li>
<li>
Item 3.2
<ul>
<li>Item 3.2.1</li>
<li>Item 3.2.2</li>
<li>Item 3.2.3</li>
</ul>
</li>
<li>Item 3.3</li>
</ul>
</li>
<li>
Item 4
<ul>
<li>Item 4.1</li>
<li>
Item 4.2
<ul>
<li>Item 4.2.1</li>
<li>Item 4.2.2</li>
</ul>
</li>
</ul>
</li>
<li>Item 5</li>
</ul>
Your code doesn't work for me in Safari. When I click on a sub-list, the top-list is toggled.
How about:
$(document).ready(function() {
$('li:has(ul)').click(function(event) {
$(this).css('list-style-image', $(this).children().is(':hidden') ? 'url(minus.gif)' : 'url(plus.gif)')
$(this).children().toggle('fast')
return false
})
.css({cursor:'pointer', 'list-style-image':'url(plus.gif)'})
.children().hide()
$('li:not(:has(ul))').click(function(event) { return false })
.css({cursor:'default', 'list-style-image':'none'})
})

Categories