I'm trying to loop a list making a carousel effect. I think it should know first the counts of all the list so it can do math. then it can do something like this. If it add on the other side it will remove something on the other side. So if I remove the first list item it will add the the last item.
$(function() {
$('#up').mouseenter(goUp);
$('#down').mouseenter(goDown);
function goUp() {
$('#list').last('li').remove;
$('#list').first('li').append('put the first item here')
}
function goDown() {
$('#list').first('li').remove;
$('#list').last('li').append('put the first item here')
}
}
<a id="up">+</a>
<ul id="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<a id="down">-</a>
You have a bunch of errors there and I think your logic is wrong. You want to use a .current class and hide() or show() the items. Something like this should work.
$items.hide().first().addClass('cur').show();
var actions = {
getCurIdx: function(){
return $items.filter('.cur').index();
},
goTo: function(idx){
// Something like...
$items
.removeClass('cur').hide()
.eq(idx).addClass('cur').show();
},
next: function(){
var _idx = actions.getCurIdx();
if(_idx < $items.length) {
actions.goTo(_idx+1);
} else {
actions.goTo(0); // Go first
}
},
prev: function(){
var _idx = actions.getCurIdx();
if(_idx >= 0) {
actions.goTo(_idx-1);
} else {
actions.goTo($items.length-1); // Go last
}
}
};
$next.click(function(){ actions.next(); });
$prev.click(function(){ actions.prev(); });
There's a few problems, first the last and first methods don't work the way you're thinking, you don't pass a selector to them (doc) so what you're actually doing is finding the last element in the matched selector of #list. What you want to be using is the :last selector (doc) (and :first), changing your selectors to.
Next remove is a method not a property, so you are missing some brackets. Also remove will return the jQuery object that you were working against so you can reposition it in the DOM.
Here is a jsfiddle that implements what you want. It uses the before and after methods to insert the new item correctly in the DOM.
Related
Please check out this fiddle: https://jsfiddle.net/willbeeler/tfm8ohmw/
HTML:
Do it! Roll me down and up again!
<ul class="roll-btns">
<li>Milk</li>
<li>Eggs and Cheese</li>
<li>Bacon and Eggs</li>
<li>Bread</li>
</ul>
jQUERY
$('.roll-btn').click(function() {
var ctrls = '.control';
if ($(ctrls).hasClass('noshow'))
{
$(ctrls).each(function() {
$(this).removeClass('noshow');
$(this).addClass('fadeInDown');
});
} else {
$(ctrls).each(function() {
$(this).removeClass('fadeInDown');
$(this).addClass('fadeOutDown');
});
}
});
This is a pretty simple thing, but I'm having trouble implementing it. Basically, the class "noshow" is a toggle for the A elements. If it does not exist, then add the CSS animation to the A element. If the CSS animation exists, add another css element to hide the A elements. I've tried delaying the "noshow" class, and other methods to no avail. This entire example works correctly with the first two clicks, but because it doesn't add the noshow class, it won't work after that. Basically, I need to add the noshow class on the second click AFTER the CSS animation gets done playing.
$('.roll-btn').click(function() {
var ctrls = '.control';
if ($(ctrls).hasClass('noshow') || $(ctrls).hasClass('fadeOutDown')) {
$(ctrls).each(function() {
$(this).removeClass('noshow');
$(this).addClass('fadeInDown');
$(this).removeClass('fadeOutDown');
});
} else {
$(ctrls).each(function() {
$(this).removeClass('fadeInDown');
$(this).addClass('fadeOutDown');
});
}
});
Trying to remove an li from a ul using angular, successfully removing the element from the array but angularJS doesn't remove the li until it is hovered over / some action is taken on that specific li. Code follows:
app.js
myApp.run(function($rootScope, appAPIservice){
appAPIservice.getInterests().success(function (response) {
$rootScope.interests = [];
if (response.data) {
var interests = response.data;
for (var i = 0; i < interests.length; i++) {
$rootScope.interests.push(interests[i]));
}
}
});
});
index.html
<ul ng-controller="interestsController">
<li ng-repeat="interest in interests">
{{interest.parentName}} / {{interest.childName}}
<button ng-click="deleteInterest($index)"></button>
</li>
</ul>
controllers.js: deleteInterest is defined here
myApp.controller('interestsController', function($scope) {
$scope.deleteInterest = function(arrayIndex) {
$scope.interests.splice(arrayIndex, 1);
});
}
});
This produces the following output on page load:
<ul class="ng-scope" ng-controller="interestsController">
<li class="ng-scope" ng-repeat="interest in interests">
Other Parent/Other Child
<button ng-click="deleteInterest($index)"><i class="icon-close"></i></button>
</li>
</ul>
The problem occurs when clicking deleteInterest() button. The following classes get added to the list item class: ng-animate, ng-leave, ng-leave-active. Unfortunately, the list item remains in the list until the item is hovered over. At that point, the list item is successfully removed from the DOM.
<li class="ng-scope ng-animate ng-leave ng-leave-active" ng-repeat="interest in interests">
Some Parent / Some Child
<button ng-click="deleteInterest($index)"><i class="icon-close"></i></button>
</li>
I've tried wrapping the interestsController.deleteInterest's
$scope.interests.splice(arrayIndex, 1); line as
$scope.$apply(function(){
$scope.interests.splice(arrayIndex, 1);
});
but I receive an error message saying that $scope.$digest is already in progress.
Is there a way to force angularJS to remove all ng-leave items?
A couple of ideas. Instead of $scope.$apply, try
$timeout(function(){
$scope.interests.splice(arrayIndex, 1);
});
$timeout will internaly trigger the digest if necessary, but won't throw that error if a digest is already in progress.
Alternatively, how about just using CSS to hide the li until angular gets round to removing it?
.ng-leave { display:none; }
I have a similar problem with ng-repeat.
After setting item array length to 0 I must run timeout with long delay:
$timeout(function(){
do_some_action
}, 2000);
Even evalAsync doesn't work:
$scope.$evalAsync(function () {
do_some_action
});
Another workaround is simply don't consider old items:
var elems = angular.element(document.getElementsByClassName('repeated_items'));
_.each(elems, function (itm) {
if (itm.$$hashKey) return true;
do_some_action
});
I am working in some exercise with Jquery that allows me to reorder some elements. The example I take to explain it here is based on the number inside some <li> tags like this:
<ul>
<li>4</li>
<li>5</li>
.
.
</ul>
Here I want to reorder and I think about some logic with this function, for each li element remove it and place it at the bottom.
$('li').each(function(){
$(this).parent().append(this);
reorder ($(this));
})
Then with this another function evaluate the value of the previous elementvar prev and if prev is lower then place the element above that one.
function reorder(p) {
var val = parseInt(p.text()),
prev = parseInt(p.prev().text());
if (val > prev) {
p.prev().before(p);
}
}
Now the question is I need if the statement is true reevaluate the values with the new prev element to consider the new position. Some this way:
if (val > prev) {
p.prev().before(p);
// And Run again the function to compare with the new prev element
}
Can someone show me the way to do this?
Here is a Fiddle Example
Pd: I now there are other options like This to get the reorder but the example goes more about the recall of the function.
Simply call it again (this is known as recursion):
if (val > prev) {
p.prev().before(p);
reorder (p);
}
Demonstration
I have jQuery read some text then add a class based on the value of this text. (The text read is rendered by my CMS).
The HTML Looks like this:
<ul class="zoneSubscriptions">
<li>
<ul>
<li class="zoneName">Professional</li>
<li>Never</li>
</ul>
</li>
<li>
<ul>
<li class="zoneName">RTTM</li>
<li>Never</li>
</ul>
</li>
</ul>
I want to read the text of class="zoneName"and add a class based on this.
JS to do this:
$(function() {
var zone = $( ".zoneName" ).text();
$( "#zones" ).addClass( zone );
});
This works without issue, however, I need it to add two classes, Professional and RTTM. What it adds is ProfessionalRTTM.
My question is how would I add the classes while keeping a space between the words?
In other words it should render like this: class="Professional RTTM" not class="ProfessionalRTTM"
Note: In my example there are two "zoneName"s. There could be anywhere from 1 to 5 or more when used live.
Try iterating the tags:
var $zones = $("#zones");
$(".zoneName").each(function () {
$zones.addClass( $(this).text() );
});
Also possible (if you want to reuse the list of class names)
var classes = [];
$(".zoneName").each(function () {
classes.push($(this).text());
});
$("#zones").addClass(classes.join(" "));
You're calling .text() on multiple results which is joining them together.
Why not do something like this instead:
var zone = $( ".zoneName" ).each(function(){
$("#zones").addClass($(this).text());
});
Find all your .zoneNames and then call addClass for each one.
jsFiddle example
You need to iterate across the .zoneNames, otherwise your .text() will be a one undivided string (unless you have whitespace in there)
$(".zoneName").each(function() {
$("#zones").addClass($(this).text());
});
You can use the callback function of addClass(), and use map() to get an array of the text, then simply join with a space:
$('#zones').addClass(function() {
return $('.zoneName').map(function() {
return $(this).text();
}).get().join(' ');
});
Here's a fiddle
I have a list that is kind of like this:
<li class="listElement semiUniqueCLassOne" unique-class="semiUniqueCLassOne" style="display:none;">one</li>
<li class="listElement semiUniqueCLassOne" unique-class="semiUniqueCLassOne" style="display:none;">two</li>
<li class="listElement semiUniqueCLassOne" unique-class="semiUniqueCLassOne" style="display:none;">three</li>
<li class="listElement semiUniqueCLassTwo" unique-class="semiUniqueCLassTwo" style="display:none;">four</li>
<li class="listElement semiUniqueCLassTwo" unique-class="semiUniqueCLassTwo" style="display:none;">five</li>
<li class="listElement semiUniqueCLassThree" unique-class="semiUniqueCLassThree" style="display:none;">six</li>
I'm trying to only show the first of each of the semiUniqueCLass so I'm attempting to do it like this:
$('.listElement').each(function(){
var uniqueClass = $(this).attr('unique-class');
if($('.'+uniqueClass).is(':first')){
$(this).show();
}
});
this doesn't work... what ways can I make it work? tried several, a bit stuck.
This should do the trick:
$('.listElement').hide();
$('.listElement:first-child').show();
If there's more elements with the listElement class:
$('.listElement[#class*=semiUniqueCLass]').hide();
$('.listElement[#class*=semiUniqueCLass]:first-child').show();
(Only shows / hides listElements that have a class that contains semiUniqueCLass)
To show the first of each unique semiUniqueCLass* item, take a look at Jack's Answer or xdazz's answer.
Just use the .first() method.
$('.listElement').each(function(){
var uniqueClass = $(this).attr('unique-class');
$('.'+uniqueClass).first().show();
});
And the working demo.
Another solution is shown as below:
var clazzs = $('.listElement').map(function() {
return $(this).attr('unique-class');
});
$.unique(clazzs).each(function() {
$('.'+this).first().show();
});
Also the working demo.
To show the first of each unique semiUniqueCLass* item, you'd have to iterate over all relevant .listElement nodes manually:
var shown = {}; // keep track of which class has already been shown
$('.listElement[unique-class]').each(function() {
var className = this.getAttribute('unique-class');
if (!shown[className]) {
// this class has not been seen before, show the first only
shown[className] = true;
$(this).show();
}
});
It shows elements one, four and six.
Update
I've pitted this answer against the two solutions by xdazz using this jsperf benchmark. This answer comes out on top with the runner up being ~30% slower (in Chrome).
This code shows the first item :
$('[class^=listElement]').first().show();