I am using a WordPress plugin that dynamically creates the following list on the fly:
<ul>
<li class="proteins"></li>
<li class="seafood"></li>
<li class="dairy"></li>
<li class="veggies"></li>
<li class="fruit"></li>
<li class="nuts-seeds"></li>
<li class="grains"></li>
<li class="pasta"></li>
<li class="spices"></li>
<li class="herbs"></li>
<li class="technique"></li>
<li class="special"></li>
<li class="servings"></li>
</ul>
I need to wrap specific lis in unique divs like this:
<ul>
<div id="main">
<li class="proteins"></li>
<li class="seafood"></li>
<li class="dairy"></li>
<li class="veggies"></li>
<li class="fruit"></li>
</div>
<div id="carbs">
<li class="nuts-seeds"></li>
<li class="grains"></li>
<li class="pasta"></li>
</div>
<div id="spice">
<li class="spices"></li>
<li class="herbs"></li>
</div>
<div id="other">
<li class="technique"></li>
<li class="special"></li>
<li class="servings"></li>
</div>
</ul>
I've read about using the wrapall() function but I can't find instructions on how to wrap a group of elements that do not have the same class.
Assuming that you take the time to define the relationship between the class-names and the eventual id under which they should appear, and that you use valid HTML, the following plain JavaScript works (albeit I think it could be refined):
var grouped = {
'main' : ['proteins','seafood','dairy','veggies','fruit'],
'carbs' : ['nuts-seeds','grains','pasta'],
'spice' : ['spices','herbs'],
'other' : ['technique','special','servings']
};
function groupBy(el, map) {
var els,
newList = document.createElement('ul'),
newListItem = document.createElement('li'),
tmpList, tmpLI;
for (var key in map) {
if (map.hasOwnProperty(key)){
els = el.querySelectorAll('.' + map[key].join(', .'));
tmpList = newList.cloneNode();
tmpLI = newListItem.cloneNode();
tmpLI.appendChild(tmpList);
tmpLI.id = key;
for (var i = 0, len = els.length; i < len; i++){
tmpList.appendChild(els[i]);
}
el.appendChild(tmpLI);
}
}
}
groupBy(document.querySelector('ul'),grouped);
JS Fiddle demo.
The above function converts:
<ul>
<li class="proteins"></li>
<li class="seafood"></li>
<li class="dairy"></li>
<li class="veggies"></li>
<li class="fruit"></li>
<li class="nuts-seeds"></li>
<li class="grains"></li>
<li class="pasta"></li>
<li class="spices"></li>
<li class="herbs"></li>
<li class="technique"></li>
<li class="special"></li>
<li class="servings"></li>
</ul>
Into:
<ul>
<li id="main">
<ul>
<li class="proteins"></li>
<li class="seafood"></li>
<li class="dairy"></li>
<li class="veggies"></li>
<li class="fruit"></li>
</ul>
</li>
<li id="carbs">
<ul>
<li class="nuts-seeds"></li>
<li class="grains"></li>
<li class="pasta"></li>
</ul>
</li>
<li id="spice">
<ul>
<li class="spices"></li>
<li class="herbs"></li>
</ul>
</li>
<li id="other">
<ul>
<li class="technique"></li>
<li class="special"></li>
<li class="servings"></li>
</ul>
</li>
</ul>
Related
I'm making a calendar with HTML, CSS and JavaScript. I want it to highlight current date automatically. Here is my code:
hday();
function hday() {
var d = new Date().getDate();
document.getElementById(d).classList.add('today');
}
.today {
background: red;
}
<ul class="days">
<li id="01">1</li>
<li id="02">2</li>
<li id="03">3</li>
<li id="04">4</li>
<li id="05">5</li>
<li id="06">6</li>
<li id="07">7</li>
<li id="08">8</li>
<li id="09">9</li>
<li id="10">10</li>
<li id="11">11</li>
<li id="12">12</li>
<li id="13">13</li>
<li id="14">14</li>
<li id="15">15</li>
<li id="16">16</li>
<li id="17">17</li>
<li id="18">18</li>
<li id="19">19</li>
<li id="20">20</li>
<li id="21">21</li>
<li id="22">22</li>
<li id="23">23</li>
<li id="24">24</li>
<li id="25">25</li>
<li id="26">26</li>
<li id="27">27</li>
<li id="28">28</li>
<li id="29">29</li>
<li id="30">30</li>
<li id="31">31</li>
</ul>
However, the current date is not highlighted. From chrome developer tools, I found that "background : red" didn't apply to the date today. How to make it work?
I just started HTML and CSS, so sorry for basic Questions.
Your issue is with order of operations. You need to call the method hday() after you define it.
You have:
hday();
function hday() {
var d = new Date().getDate();
document.getElementById(d).classList.add('today');
}
You need:
function hday() {
var d = new Date().getDate();
document.getElementById(d).classList.add('today');
}
hday();
.today {
background: red;
}
<ul class="days">
<li id="01">1</li>
<li id="02">2</li>
<li id="03">3</li>
<li id="04">4</li>
<li id="05">5</li>
<li id="06">6</li>
<li id="07">7</li>
<li id="08">8</li>
<li id="09">9</li>
<li id="10">10</li>
<li id="11">11</li>
<li id="12">12</li>
<li id="13">13</li>
<li id="14">14</li>
<li id="15">15</li>
<li id="16">16</li>
<li id="17">17</li>
<li id="18">18</li>
<li id="19">19</li>
<li id="20">20</li>
<li id="21">21</li>
<li id="22">22</li>
<li id="23">23</li>
<li id="24">24</li>
<li id="25">25</li>
<li id="26">26</li>
<li id="27">27</li>
<li id="28">28</li>
<li id="29">29</li>
<li id="30">30</li>
<li id="31">31</li>
</ul>
I have a list of cities in HTML. I have a list of things to do in HTML. When a button is clicked, I want to create a combined list where each city has a child list that contains the list of all the things to do.
See the Fiddle.
Currently the list of things to do only displays as a child of the last city instead of each of them. What am I doing wrong?
After the button click, I want the output to be as follows:
<div id="combined">
<ul id="cityList">
<li>
<h3>New York</h3>
<ul class="thingsToDoList">
<li class="todo">Restaurants</li>
<li class="todo">Theatres</li>
<li class="todo">Museums</li>
</ul>
</li>
</li>
<li>
<h3>Seattle</h3>
<ul class="thingsToDoList">
<li class="todo">Restaurants</li>
<li class="todo">Theatres</li>
<li class="todo">Museums</li>
</ul>
</li>
</li>
<li>
<h3>Los Angeles</h3>
<ul class="thingsToDoList">
<li class="todo">Restaurants</li>
<li class="todo">Theatres</li>
<li class="todo">Museums</li>
</ul>
</li>
</li>
<li>
<h3>Atlanta</h3>
<ul class="thingsToDoList">
<li class="todo">Restaurants</li>
<li class="todo">Theatres</li>
<li class="todo">Museums</li>
</ul>
</li>
</ul>
</div>
But currently the output looks like this:
<div id="combined">
<ul id="cityList">
<li>
<h3>New York</h3>
<ul class="thingsToDoList"></ul>
</li>
<li>
<h3>Seattle</h3>
<ul class="thingsToDoList"></ul>
</li>
<li>
<h3>Los Angeles</h3>
<ul class="thingsToDoList"></ul>
</li>
<li>
<h3>Atlanta</h3>
<ul class="thingsToDoList">
<li class="todo">Restaurants</li>
<li class="todo">Theatres</li>
<li class="todo">Museums</li>
</ul>
</li>
</ul>
</div>
Here's my current HTML
<div id="cities">
<ul>
<li class="city">New York</li>
<li class="city">Seattle</li>
<li class="city">Los Angeles</li>
<li class="city">Atlanta</li>
</ul>
</div>
<div id="thingsToDo">
<ul>
<li class="todo">Restaurants</li>
<li class="todo">Theatres</li>
<li class="todo">Museums</li>
</ul>
</div>
<button id="combine">Combine</button>
<div id="combined">
<ul id="cityList">
</ul>
</div>
And my current JS
const cities = document.getElementById('cities');
const thingsToDo = document.getElementById('thingsToDo');
const combine = document.getElementById('combine');
const combined = document.getElementById('combined');
const cityList = document.getElementById('cityList');
combine.addEventListener('click', function(e){
let allCities = document.querySelectorAll('li.city');
[].forEach.call(allCities, function(city){
let eachCity = document.createElement('li');
let cityHeader = document.createElement('h3');
cityHeader.innerText = city.innerText;
cityList.appendChild(eachCity);
let thingsToDoList = document.createElement('ul');
thingsToDoList.setAttribute('class', 'thingsToDoList');
eachCity.appendChild(cityHeader);
eachCity.appendChild(thingsToDoList);
}); // close allCities loop
let newThingsList = document.querySelectorAll('.thingsToDoList');
[].forEach.call(newThingsList, function(tList){
let allThingsToDo = document.querySelectorAll('li.todo');
[].forEach.call(allThingsToDo, function(thing){
tList.appendChild(thing);
})
})
});
Any help would be greatly appreciated.
I think what's happening is you are appending the same node over and over and it ends up on the last one? (rather than creating a new node or cloning the node) You can use .cloneNode(true) on your selector to actually clone the node. I put together a shorter example that does this. Not sure if it meets all the requirements, but hopefully it's helpful.
Note: this example, like yours will keep adding more if you keep clicking the button. Logic would need to be added to prevent this if that what you wanted.
const cities = document.getElementById('cities');
const thingsToDo = document.getElementById('thingsToDo');
const combine = document.getElementById('combine');
const combined = document.getElementById('combined');
const cityList = document.getElementById('cityList');
combine.addEventListener('click', function(e){
let allCities = document.querySelectorAll('li.city');
allCities.forEach(element => {
let cityLi = document.createElement('li');
let cityHeader = document.createElement('h3');
cityHeader.innerText = element.innerText
cityList.appendChild(cityHeader);
cityList.appendChild(cityLi);
cityList.appendChild(thingsToDo.cloneNode(true));
});
});
<div id="cities">
<ul>
<li class="city">New York</li>
<li class="city">Seattle</li>
<li class="city">Los Angeles</li>
<li class="city">Atlanta</li>
</ul>
</div>
<div id="thingsToDo">
<ul>
<li class="todo">Restaurants</li>
<li class="todo">Theatres</li>
<li class="todo">Museums</li>
</ul>
</div>
<button id="combine">Combine</button>
<div id="combined">
<ul id="cityList">
</ul>
</div>
I want to select only the element li with class options or level in jquery
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<div class="root-head">
<ul>
<li class="level" name="name1">main</li>
<li class="level options" name="name2">
sub1
<ul class="option-selector lv1">
<li value="1">option1</li>
<li value="2">option2</li>
<li value="3">option2</li>
<li value="4">option3</li>
<li value="5">option4</li>
<li value="6">option5</li>
</ul>
</li>
<li class="level options betimes" name="name3">
sub2
</li>
<li class="level options betimes" name="name4">
sub3
</li>
</ul>
</div>
How can make the function below called only then I click on sub1 text, not all of the li element like <li value="1">option1</li>
$("li[name=name2]").click(function () {
console.log("Funtion called");
if (/*suplimentary condition*/) {
console.log("Yes is here");
}
});
Maybe is useful the last my question!
You have to stop the event propagation in the child li click event
$(".level").click(function () {
console.log("Funtion called");
});
$(".level ul").click(function (e) {
e.stopPropagation();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<div class="root-head">
<ul>
<li class="level" name="name1">main</li>
<li class="level options" name="name2">
sub1
<ul class="option-selector lv1">
<li value="1">option1</li>
<li value="2">option2</li>
<li value="3">option2</li>
<li value="4">option3</li>
<li value="5">option4</li>
<li value="6">option5</li>
</ul>
</li>
<li class="level options betimes" name="name3">
sub2
</li>
<li class="level options betimes" name="name4">
sub3
</li>
</ul>
</div>
You can check if event.target is the same as this.
event.target will be the actually clicked element.
this will be the element the click function is bound too.
According to the jquery docs it's a proper method of detecting event bubbling.
$("li[name=name2]").click(function(e) {
if (e.target === this) {
console.log("sub clicked");
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<div class="root-head">
<ul>
<li class="level" name="name1">main</li>
<li class="level options" name="name2">
sub1
<ul class="option-selector lv1">
<li value="1">option1</li>
<li value="2">option2</li>
<li value="3">option2</li>
<li value="4">option3</li>
<li value="5">option4</li>
<li value="6">option5</li>
</ul>
</li>
<li class="level options betimes" name="name3">
sub2
</li>
<li class="level options betimes" name="name4">
sub3
</li>
</ul>
</div>
be more specific - give the target some uniqueness in the dom
<li class="level options" name="name2">
<div>sub1</div>
<ul class="option-selector lv1">
<li value="1">option1</li>
<li value="2">option2</li>
<li value="3">option2</li>
<li value="4">option3</li>
<li value="5">option4</li>
<li value="6">option5</li>
</ul>
</li>
$('li[name="name2"] > div').click(function () {
console.log("Funtion called");
if (/*suplimentary condition*/) {
console.log("Yes is here");
}
});
I have a menu populating from MySQL table and PHP up to some nested sub levels.
my menu like this:
A
B
C
If click on A first time it is showing all the child elements and again I click child elements of A it displaying child elements also fine.
But the problem is when I click on the B after open all the levels items of A it shows B sub elements fine. But again if I click A it showing all the elements except child child elements also.
I used jQuery for this.
So I want to bring back to the original state? ( only expand the top child elements, not the sub child elements ),
how to do that?
//this is my jquery code for elements clickable in menu.
$(document).ready(function() {
$(".lichild").parent().hide();
$(".limain").click(function() {
$(this).children('ul').show();
$(this).siblings(".limain").children('ul').hide();
});
$(".lichild").click(function() {
$(this).children('ul').show();
$(this).siblings().children('ul').hide()
});
});
<!-- This is the html I am generating using a PHP function -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li class="limain">A
<ul>
<li class="lichild">A1
<ul>
<li class="lichild">a2</li>
<li class="lichild">a1
<ul>
<li class="lichild">aaaaaa</li>
<li class="lichild">abbbbbb</li>
</ul>
</li>
</ul>
</li>
<li class="lichild">A2</li>
<li class="lichild">A3</li>
<li class="lichild">A4</li>
<li class="lichild">A5</li>
</ul>
<li class="limain">B
<ul>
<li class="lichild">B1</li>
<li class="lichild">B2</li>
</ul>
</li>
<li class="limain">C
<ul>
<li class="lichild">C1</li>
<li class="lichild">C2</li>
<li class="lichild">C3</li>
<li class="lichild">A6
<ul>
<li class="lichild">A8
<ul>
<li class="lichild">A10
<ul>
<li class="lichild">A13
</li>
<li class="lichild">A14
</li>
</ul>
</li>
<li class="lichild">A11
</li>
</ul>
</li>
<li class="lichild">A9
</li>
</ul>
</li>
<li class="lichild">A7
</li>
</ul>
</li>
<li class="limain">D
<ul>
<li class="lichild">D1</li>
<li class="lichild">D2
</li>
</ul>
</li>
</ul>
Use find inside siblings and hide it.
$(".lichild").parent().hide();
$(".limain").click(function() {
$(this).children('ul').show();
$(this).siblings(".limain").find('ul').hide(); // Change in this line
});
$(".lichild").click(function() {
$(this).children('ul').show();
$(this).siblings().children('ul').hide()
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<ul>
<li class="limain">
A
<ul>
<li class="lichild">
A1
<ul>
<li class="lichild">a2</li>
<li class="lichild">
a1
<ul>
<li class="lichild">aaaaaa</li <li class="lichild">abbbbbb</li>
</ul>
</li>
</ul>
</li>
<li class="lichild">A2</li>
<li class="lichild">A3</li>
<li class="lichild">A4</li>
<li class="lichild">A5</li>
</ul>
<li class="limain">
B
<ul>
<li class="lichild">B1</li>
<li class="lichild">B2</li>
</ul>
</li>
<li class="limain">
C
<ul>
<li class="lichild">C1</li>
<li class="lichild">C2</li>
<li class="lichild">C3</li>
<li class="lichild">
A6
<ul>
<li class="lichild">
A8
<ul>
<li class="lichild">
A10
<ul>
<li class="lichild">A13
</li>
<li class="lichild">A14
</li>
</ul>
</li>
<li class="lichild">A11
</li>
</ul>
</li>
<li class="lichild">A9
</li>
</ul>
</li>
<li class="lichild">A7
</li>
</ul>
</li>
<li class="limain">
D
<ul>
<li class="lichild">D1</li>
<li class="lichild">D2
</li>
</ul>
</li>
</ul>
$(document).ready(function () {
$(".lichild").parent().hide();
$(".limain").click(function () {
$(this).children('ul').show();
$(this).siblings().find('ul').hide();
});
$(".lichild").click(function () {
$(this).children('ul').show();
$(this).siblings('li').find('ul').hide()
});
});
I have a list of elements that each have a data-name attribute. I want to select all elements that do not start with the prefix "arc" and hide them.
<ul id="slides">
<li data-name="arc_1">...</li>
<li data-name="arc_2">...</li>
<li data-name="biz_1">...</li>
<li data-name="biz_2">...</li>
<li data-name="cas_1">...</li>
<li data-name="cas_2">...</li>
<li data-name="cas_3">...</li>
<li data-name="prd_1">...</li>
</ul>
I have tried to use $('#slides li[data-name^="arc"]') but it selects the opposite of what it need.
Use :not():
$('#slides li:not([data-name^="arc"])')
$('#slides li:not([data-name^="arc"])').css('color','red')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul id="slides">
<li data-name="arc_1">...</li>
<li data-name="arc_2">...</li>
<li data-name="biz_1">...</li>
<li data-name="biz_2">...</li>
<li data-name="cas_1">...</li>
<li data-name="cas_2">...</li>
<li data-name="cas_3">...</li>
<li data-name="prd_1">...</li>
</ul>