I have myself an unordered list:
<ul>
<li ng-class="{ 'active': selected }" ng-click="selected = !selcted">Stuff 1</li>
<li ng-class="{ 'active': selected }" ng-click="selected = !selcted">Stuff 2</li>
<li ng-class="{ 'active': selected }" ng-click="selected = !selcted">Stuff 3</li>
</ul>
I want to apply a class to a list item a user selects. Only one item can have that class at a time. What's the best angular-ish way to approach this? There are about a dozen elements or so, so I'd rather not declare a new variable for each item.
Set a property for each item:
<ul>
<li ng-class="{ 'active': selected == 1 }" ng-click="selected = 1">Stuff 1</li>
<li ng-class="{ 'active': selected == 2 }" ng-click="selected = 2">Stuff 2</li>
<li ng-class="{ 'active': selected == 3 }" ng-click="selected = 3">Stuff 3</li>
</ul>
If you want angular-ish use ng-repat
HTML
<ul ng-repeat="s in stuffs">
<li ng-class="{active:$index==selected}" ng-click="selected=$index">{{s}}</li>
</ul>
JS
$scope.stuffs = ['Stuff 1', 'Stuff 2', 'Stuff 3'];
Related
In a Vue-component, I have a menu like this:
<ul class="menu-outer-wrapper">
<li>Foo 1</li>
<li class="has-children">
Foo 2
<ul>
<li>Child 1</li>
<li>Child 2</li>
<li>Child 3</li>
</ul>
</li>
<li>Foo 5</li>
<li class="has-children">
Foo 6
<ul>
<li>Child 1</li>
<li>Child 2</li>
</ul>
</li>
<li>Foo 7</li>
<li>Foo 8</li>
</ul>
And I would like to add the class hovered to the li.has-children-elements upon hover (mouseenter) (to be able to make some nicer animations for the children of that dropdown. And remove that class on mouseleave.
I know that there are options to do this with pure CSS, - but controlling delays and soft fade-in's are a pain (and become very messy very fast, without adding some classes).
I imagined doing something like this:
...
mounted(){
let liWithChildren = document.querySelectorAll( '.menu-outer-wrapper > li.has-children' );
liWithChildren.forEach( (event, window) => {
// Somehow add the class to the hovered element here.
// But I don't know how. Or if it's a good/bad idea (performance-wise).
}
}
But is that the way to go? And can I do it without using data (since the menu is dynamically generated by a CMS-system.
Update 1
I'm trying to keep my markdown readable. So I would like to avoid something like this:
<ul class="menu-outer-wrapper">
<li :class="[ { 'hovered' : someVar } ]">
Foo 1
</li>
<li :class="[ { 'hovered' : someVar }, 'has-children' ]">
Foo 2
<ul>
<li>Child 1</li>
<li>Child 2</li>
<li>Child 3</li>
</ul>
</li>
<li :class="[ { 'hovered' : someVar } ]">
Foo 2
</li>
...
...
...
Both since it won't gel with the dynamically generated menu.
And also since it add a lot of noise to the markdown.
Update 2
I simplified the example, to make it easier to digest. But due to the comments I figured I would elaborate on the dynamic generated menu. I'm making it something like this:
<nav id="secondary-menu" v-if="secondaryMenu">
<ul>
<li
:class="[ { 'has-children': r.children } ]"
v-for="(r, r_key, r_index) in secondaryMenu">
<a :href="r.url" :title="r.title">
{{ r.title }}
</a>
<ul class="children" v-if="r.children">
<li v-for="(c1, c1_key, c1_index) in r.children">
<a :href="c1.url" :title="c1.title">
{{ c1.title }}
</a>
</li>
</ul>
</li>
</ul>
</nav>
You just need the #mouseenter and #mouseleave events. All you need to do is listen for the appropriate events on all list-items that could have children, then perform your class addition (or removal) if the target element has the class of "has-children". Here's how I would do it:
<template>
<nav id="secondary-menu" v-if="secondaryMenu">
<ul>
<li
:class="[{ 'has-children': r.children }]"
v-for="(r, r_key, r_index) in secondaryMenu"
:key="`${r_key}-${r_index}`"
#mouseenter="addClass"
#mouseleave="removeClass"
>
<a :href="r.url" :title="r.title">
{{ r.title }}
</a>
<ul class="children" v-if="r.children">
<li
v-for="(c1, c1_key, c1_index) in r.children"
:key="`${c1_key}-${c1_index}`"
>
<a :href="c1.url" :title="c1.title">
{{ c1.title }}
</a>
</li>
</ul>
</li>
</ul>
</nav>
</template>
<script>
export default {
name: "HoverNav",
props: {
secondaryMenu: {
type: Array,
required: true,
},
},
methods: {
addClass: function (e) {
if (e.target.classList.contains("has-children")) {
e.target.classList.add("hovered");
}
},
removeClass: function (e) {
if (e.target.classList.contains("has-children")) {
e.target.classList.remove("hovered");
}
},
},
};
</script>
Here's a very unaesthetic sandbox of this in action. Let me know if this works for you :)
https://codesandbox.io/s/headless-brook-ysq97?file=/src/components/HoverNav.vue:0-1169
I have some listings.
Currently id is test_3, test_1, test_2. I need number (3,1,2) from id of each li and append this number in to another data attribute. Please check the result section. It will give you an idea about what I am expecting. Thanks
Html
<ul id="cat">
<li id="test_3">Text 3</li>
<li id="test_1">Text 1</li>
<li id="test_2">Text 2</li>
</ul>
Script
$('#cat').attr('id').split("-")[2];
Expected result
<ul id="cat">
<li id="test_3" data-id="3">Text 3</li>
<li id="test_1" data-id="1">Text 1</li>
<li id="test_2" data-id="2">Text 2</li>
</ul>
To achieve this you can loop over the li elements and set the data() based on the number in the id attribute. Try this:
$('#cat li').each(function() {
$(this).data('id', this.id.split('_')[1]);
}).click(function() {
console.log($(this).data('id'));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="cat">
<li id="test_3">Text 3</li>
<li id="test_1">Text 1</li>
<li id="test_2">Text 2</li>
</ul>
You can loop through all of the children of the ul element with the id cat.
On each loop, get the ID by getting the id attribute, splitting it on _ and getting the first index.
You can then set the attribute by using JQuery's attribute function, which you can learn more about here.
$('#cat li').each(function () {
var dataId = $(this).attr('id').split('_')[1];
$(this).attr('data-id', dataId);
});
An example of this in action
$('#cat li').each(function () {
var dataId = $(this).attr('id').split('_')[1];
$(this).attr('data-id', dataId);
});
console.log($('#cat').html());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="cat">
<li id="test_3">Text 3</li>
<li id="test_1">Text 1</li>
<li id="test_2">Text 2</li>
</ul>
This should some your problem.
Using $(this).attr("id").split('_') will split "cat_3" into "cat" and "3" then using [1] after will select "3"
$("#cat li").each(function(){
$(this).attr("data-id", $(this).attr("id").split('_')[1]);
})
console.log($("#cat").html())
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul id="cat">
<li id="test_3">Text 3</li>
<li id="test_1">Text 1</li>
<li id="test_2">Text 2</li>
</ul>
You can also use a RegExpression.
var suffix = "test_2".match(/\d+/); // results --> 2
Basically it fetches out numeric value within a string value.
Usage
$('#cat li').each(function() {
$(this).data('id', this.id.match(/\d+/));
})
I have the following code:
<ul class="list-group">
<li class="list-group-item" ng-repeat="item in items | filter:searchText" ng-click="item.expanded = !item.expanded">
{{item.name}} {{templatefolder.expanded}}
<ul ng-show="item.expanded" class="list-group-item">
<li class="list-group-item" ng-class="{'active' : item.id == document.itemId}" ng-repeat="folder in item.folders" ng-click="document.itemId= item.id">
{{folder.name}}
</li>
</ul>
</li>
</ul>
That code works. But, when i click a 'sub item' (item.folders.name) the li is collapsing because he is in the li with the ng-click function.
Is there a way to show the item.folders after a click on a item?
Add $event.stopPropagation(); to your child li so the event doesn't propagate to the parent.
ng-click="document.itemId= item.id; $event.stopPropagation();">
Actually I'm really very sorry about my question, I'm not sure how to make attention or ask question about this kind of problem.
Please see my code 1st.
<div data-model="ABC123" id="product">Select a Product</div>
<ul id="lists">
<li data-product="P1">Product 1
<ul id="sublists">
<li data-item="it1">P1 Item 1</li>
<li data-item="it2">P1 Item 2</li>
<li data-item="it3">P1 Item 3</li>
<li data-item="it4">P1 Item 4</li>
</ul>
</li>
<li data-product="P2">Product 2
<ul>
<li data-item="it1">P2 Item 1</li>
<li data-item="it2">P2 Item 2</li>
<li data-item="it3">P2 Item 3</li>
<li data-item="it4">P2 Item 4</li>
</ul>
</li>
<li data-product="P3">Product 3</li>
<li data-product="P4">Product 4</li>
</ul>
<div id="codes">
<span class="code1"></span>
<span class="code2"></span>
<span class="code3"></span>
</div>
The jquery code is:
<script>$('#product').click(function () {
var pmodel = $(this).data('model');
$('.code1').empty().append(pmodel);
$('.code2').empty();
$('.code3').empty();
});
$('#lists li').click(function () {
var dmodel = $(this).data('product');
$('.code2').empty().append(dmodel);
});
$('#lists li ul li').click(function () {
var item = $(this).data('item');
$('.code3').empty().append(item);
});</script>
You may directly view this at http://codepen.io/alshedupur/pen/YqKGqV
Everything is work fine, but my problem is, when I trigger parent list item like: Product 1 / Product 2 / Product 3
In result I want to empty .code3 span
I try to use $('.code3').empty(); on 2nd action but if I use this, then 3rd action I mean sub list click function not work.
Please see my screenshot for clearly understand what I want:
You need to empty .code3 as well.
$('#product').click(function() {
var pmodel = $(this).data('model');
$('.code1').empty().append(pmodel);
$('.code2').empty();
$('.code3').empty();
});
$('#lists > li').click(function(e) {
e.stopPropagation();
var dmodel = $(this).data('product');
$('.code2').empty().append(dmodel);
$('.code3').empty();
});
$('#lists li ul li').click(function(e) {
e.stopPropagation();
var item = $(this).data('item');
var dmodel = $(this).parents("li").data('product');
$('.code2').empty().append(dmodel);
$('.code3').empty().append(item);
});
#product:hover,
li:hover {
cursor: pointer
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-model="ABC123" id="product">Select a Product</div>
<ul id="lists">
<li data-product="P1">Product 1
<ul id="sublists">
<li data-item="it1">P1 Item 1</li>
<li data-item="it2">P1 Item 2</li>
<li data-item="it3">P1 Item 3</li>
<li data-item="it4">P1 Item 4</li>
</ul>
</li>
<li data-product="P2">Product 2
<ul>
<li data-item="it1">P2 Item 1</li>
<li data-item="it2">P2 Item 2</li>
<li data-item="it3">P2 Item 3</li>
<li data-item="it4">P2 Item 4</li>
</ul>
</li>
<li data-product="P3">Product 3</li>
<li data-product="P4">Product 4</li>
</ul>
<div id="codes">
<span class="code1"></span>
<span class="code2"></span>
<span class="code3"></span>
</div>
I create this nested list to show and hide items but I want ask how can I show one list and hide other for example if user click on second subject will hide all open items
HTML
<ul>
<li class="subject">List item 1 with subitems:
<ul id="item">
<li>Subitem 1</li>
<li>Subitem 2</li>
</ul>
</li>
<li class="subject">List item 2 with subitems:
<ul id="item">
<li>Subitem 1</li>
<li>Subitem 2</li>
</ul>
</li>
<li class="subject">List item 3 with subitems:
<ul id="item">
<li>Subitem 1</li>
<li>Subitem 2</li>
</ul>
</li>
</ul>
javascript
$(function(){
// $("ul li").children().slideDown("slow");
$(".subject").click(function(){
$(this).find("#item").slideToggle("slow");
});
})
CSS
#item
{
display: none;
}
$(".subject").click(function(){
$(this).find("#item").slideToggle("slow");
$(this).siblings().children('ul').slideUp();
});
DEMO
or
$(".subject").click(function () {
$(this).find("#item").slideToggle("slow")
.end().siblings().children('ul').slideUp();
});
DEMO
I corrected your markup. Your IDs should always be unique; otherwise use classes.
$(function(){
$(".subject").click(function(){
$('ul.item').not( $(this).find('ul.item') ).slideUp("slow");
$(this).find('ul.item').slideDown('slow');
});
});
WORKING JSFIDDLE DEMO