iterating through an array containing two elements results in three objects - javascript

I'm trying to iterate through an array and clone and append an element to a div element for each item in the array.
Everything is working except when it's more than one element I get some unexpected results.
The array contains two element's and I've checked that the each loop only runs two times, but for some reason I get a third element in the result.
Am I using clone() and appendTo() correctly?
each loop:
let items = $(contentWrap).find(".lc-rating-modal-review-items-wrap");
$(items).empty();
$.each(data.items, function (index, review) {
let item = GenerateReviewItem(review);
$(item).appendTo(items);
});
GenerateReviewItem:
function GenerateReviewItem(review) {
let result = $(wrap).find(".lc-rating-review-item-template").clone();
$(result).find(".lc-rating-review-item-template-date").html(review.dateFormated);
$(result).find(".lc-rating-review-item-body-wrap").html(review.review);
$(result).find(".lc-rating-review-item-template-stars-rating-label").html("(" + review.rating + ")");
$(result).find(".lc-rating-review-item-template-star").each(function (index, star) {
if (review.rating >= (index + 1)) {
$(star).removeClass("fa-star-o").addClass("fa-star");
}
});
return result;
}
Html to clone:
<div style="display:none;">
<div class="lc-rating-review-item-template">
<div class="lc-rating-review-item-header-wrap">
<div class="lc-rating-review-item-template-stars-wrap">
<div>
<i class="fa fa-star-o lc-rating-review-item-template-star" aria-hidden="true"></i>
</div>
<div>
<i class="fa fa-star-o lc-rating-review-item-template-star" aria-hidden="true"></i>
</div>
<div>
<i class="fa fa-star-o lc-rating-review-item-template-star" aria-hidden="true"></i>
</div>
<div>
<i class="fa fa-star-o lc-rating-review-item-template-star" aria-hidden="true"></i>
</div>
<div>
<i class="fa fa-star-o lc-rating-review-item-template-star" aria-hidden="true"></i>
</div>
<div>
<span class="lc-rating-review-item-template-stars-rating-label"></span>
</div>
</div>
<div style="text-align:right;">
<span class="lc-rating-review-item-template-date"></span>
</div>
</div>
<div class="lc-rating-review-item-body-wrap"></div>
</div>
</div>

I'd recommend storing the reference to your template node outside of your loop, and then doing the cloning inside of the loop.
Define this outside the loop:
let template = $(wrap).find(".lc-rating-review-item-template")
And then change this:
let result = $(wrap).find(".lc-rating-review-item-template").clone();
To this:
let result = template.clone();
As it is currently, when your loop executes a second time, $(wrap).find(".lc-rating-review-item-template") is likely finding two items instead of one.
It also appears that wrap, unless it is a global, is undefined within the context of the GenerateReviewItem(review) function.

Related

How to make an icon slideshow with jQuery

Hi I am building a small slideshow of icons and also want to have data inside the icons like its speed and color. I've loaded jQuery from the top of my page.
<body>
<div class="main-container">
<div class="object-container">
<div class="icon-container">
<i class="fa fa-car" id="active"></i>
<i class="fa fa-bicycle" id="not-active"></i>
<i class="fa fa-plane" id="not-active"></i>
<i class="fa fa-ship" id="not-active"></i>
<i class="fa fa-fighter-jet" id="not-active"></i>
<i class="fa fa-space-shuttle" id="not-active"></i>
</div>
<div class="arrow-buttons">
</div>
</div>
</div>
</body>
Here is the jquery to press the right arrow button to change slide across the icons but it doesnt seem to be working?
$(document).ready(function(){
$('.right-arrow').on('click', function(){
let currentImg = $('#active');
let nextImg = currentImg.next();
if(currentImg.length){
currentImg.removeAttr('#active').css('z-inex', -10)
nextImg.attr('#active').css('z-index', 10);
}
});
});
First of all, I recommend using class instead of id. Even if you are going to use id, it is not recommended to use this way. The id value must be uniq. In your case, your ID's are not uniq. And also this is better to use like this ID selector.
id="notActive"
You can just try with each function to get current div. I tried with your code but did not work, with each yes.
$('.fa').each(function(){
if( $(this).hasClass('active') ){
currentImg = $(this);
}
})
And bonus : You can also use current div index value, then you dont have to use prev, next. You can increase or decrease index the value one by one. Just a suggestion.
You are using the id attribute incorrectly, as every id must be unique.
Also, setting the z-index on elements has no effect.
Finally, jQuery is not helping and can be eliminated.
Here's a more standard way to make something active/inactive, using a class named `active':
// control buttons
const right = document.querySelector('.right-arrow');
// handle right click
right.onclick = function() {
const activeImg = document.querySelector('i.active');
const nextImg = activeImg.nextElementSibling;
if (activeImg && nextImg) {
activeImg.classList.remove('active');
nextImg.classList.add('active');
}
};
i {
font-size: 32px;
z-index: -10; /* this has no effect */
}
.active {
z-index: 10;
background-color: yellow;
}
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous" />
<div class="main-container">
<div class="object-container">
<div class="icon-container">
<i class="fa fa-car active"></i>
<i class="fa fa-bicycle"></i>
<i class="fa fa-plane"></i>
<i class="fa fa-ship"></i>
<i class="fa fa-fighter-jet"></i>
<i class="fa fa-space-shuttle"></i>
</div>
<div class="arrow-buttons">
right-arrow
</div>
</div>
</div>

Remove input by clicking times icon

I have the following code which adds divs within a container (#subtask_container) via clicking a link (similar to Google Tasks):
HTML:
<p class="text-muted">
<i class="fas fa-tasks mr-2"></i>
Add subtasks
</p>
<div id="subtask_container"></div>
JS (this successfully adds unique inputs within the subtask container div along with a clickable x after each input)
var i = 1
$("#add_subtask").click(function () {
$("#subtask_container").append('<input name="subtask'+i+'" class="mt-1" id="subtask'+i+'" placeholder="Enter subtask"><i class="fas fa-times ml-1 text-muted"></i><br>');
i++;
});
What logic do I need to add to the x class to remove it's associated input?
I've tried
$('.fa-times').click(function(){
$(this).prev('input').remove();
});
but it doesn't seem to work...
Thanks!
You can simply wrap your subtask append in a div and simply use .parent() and .remove() function on that. No need to use <br>
Also, do not use .fa-times as primary click event handler as you might have other fa-times on the same page as well Which might cause issues later on. Add a custom class to your fa item (.subtask_remove_icon)
Live Demo:
var i = 1
$("#add_subtask").click(function() {
$("#subtask_container").append('<div><input name="subtask' + i + '" class="mt-1" id="subtask' + i + '" placeholder="Enter subtask"><i class="fas fa-times ml-1 text-muted subtask_remove_icon"></i></div>');
i++;
});
$(document).on('click', '.subtask_remove_icon', function() {
$(this).parent().remove(); //remove the input when X clicked
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
<p class="text-muted">
<i class="fas fa-tasks mr-2"></i>
Add subtasks
</p>
<div id="subtask_container"></div>
The event handler gets attached to all elements that are on the page load. Since the icons are appended, the right way to do this would be the following:
var i = 1
$("#add_subtask").click(function () {
$("#subtask_container").append('<div><input name="subtask'+i+'" class="mt-1" id="subtask'+i+'" placeholder="Enter subtask"><i class="fas fa-times ml-1 text-muted removeIcon"></i><br></div>');
i++;
});
$(document).on('click', '.removeIcon', function(){
$(this).parent().remove();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://kit.fontawesome.com/a076d05399.js"></script>
<p class="text-muted">
<i class="fas fa-tasks mr-2"></i>
Add subtasks
</p>
<div id="subtask_container"></div>

How to toggle a class in my code, and what am I doing wrong?

I have CSHTML code as follows. I need to toggle a class on click, which is given in the below Javascript code.
<div class="accordion bar-heading-padding" id="#accordion2" onclick="getclassofitag(this)">
<div class="accordion-group">
<div class="accordion-heading datalist" data-toggle="collapse" data-parent="#accordion2" href="##i.Id">
#*<i class="fa fa-chevron-up table-middle icon-width" aria-hidden="false"></i>*#
<i class="fa fa-chevron-down table-middle icon-width" aria-hidden="true"></i>
<h1 class="accordion-toggle table-middle">
#Html.Raw(#i.Name)
</h1>
</div>
<div id="#i.Id" class=" collapse out">
<div class="accordion-inner">
<img ng-src="#i.ImagePath" alt="logo" class="img-thumbnail img-wrap content-image" />
<h1>#Html.Raw(#i.Name)</h1>
<h3>#Html.Raw(#i.Title)</h3>
<br />
#Html.Raw(#i.BioDetail)
</div>
</div>
</div>
</div>
and the Javascript code for toggling class of the i element.
function getclassofitag(element) {
iTags = element.getElementsByTagName("i");
iTags.classList.toggle('fa-chevron-down');
itags.classList.toggle('fa-chevron-up');
}
I want to toggle the classes of the i element (<i) on click. What code do I have to write to do that? Where did I go wrong?
document.getElementsByTagName() returns an HTMLCollection object which contains a collection of DOM objects. The collection itself does not have a .classList property (like you are trying to do). That is a property of a DOM element, not the collection object. Thus, your iTags variable does not have a .classList` property on it.
So, you need to iterate the iTags HTMLCollection and use .classList on each individual DOM element.
function getclassofitag(element) {
var item, iTags = element.getElementsByTagName("i");
for (var i = 0; i < iTags.length; i++) {
item = iTags[i];
item.classList.toggle('fa-chevron-down');
item.classList.toggle('fa-chevron-up');
}
}
Note: I also declared your iTags variable as a local variable so you aren't using an implicit global (which is a bad practice).
hope this will solve your problem
$(document).ready(function () {
$('.collapse').on('shown.bs.collapse', function () {
$(this).parent().find(".fa-chevron-down").removeClass("fa-chevron-down").addClass("fa-chevron-up");
});
$('.collapse').on('hidden.bs.collapse', function () {
$(this).parent().find(".fa-chevron-up").removeClass("fa-chevron-up").addClass("fa-chevron-down");
});
});

How to get a DIV element's value, put them into array and use them later. JS/Jquery

I'd like to get the data-league values of all of those divs, and put them into an array.
What I want to do: Get all those values, save them, and then loop through them, and call a .click on those divs.
I don't know if there's an easier way to do this.
I guess it also has to loop through the divs where ID=128, and find the data-league values, right?
Honestly I'm completely stuck, so any help whatsoever would be appriciated.
Thank you.
Extra info: Basically the end result being, on click of a button, all those 'thumbnails' should open in a new tab. All tabs being different streams.
(See image)
In the example you provided the same id (128) has been used multiple times. This is not allowed. Iterating through the ids will not work.
This means you have to look for another possibility. Getting all div elements with a certain class would be possible, like in the example:
var divs = document.getElementsByClassName('videoPanel');
Now you can iterate through this collection, extract the values for the attribute data-league and save them somewhere, for example in an array:
var dataLeagueValues = [];
for (var i = 0; i < divs.length; i++) {
dataLeagueValues.push(divs[i].getAttribute('data-league'));
}
Now you have all values in the array dataLeagueValues. You can use them further in your script.
You could use the $('[attribute]') selector to select elements with data-league, you can then use .each(); to loop them.
The following example triggers a click on every element with a data-league attribute:
$('[data-league]').each(function() {
$(this).trigger('click');
});
You could also make the selector more specific to only match these elements by their shared class name videoPanel e.g.
$('.videoPanel[data-league]').each(...);
Will target all elements with a class of videoPanel and an attribute data-league
To match what you need the full example would be:
var leagues = [];
$('.videoPanel[data-league]').each(function() {
leagues.push($(this).attr('data-league'));
});
Following code snippet will your insert all data-league into an array.
var leagues=[];
$('.vedioPnnel').each(function() {
var league = $(this).data('league');
leagues.push(league);
});
console.log(leagues);
I propose to use the javascript forEach function like in the folowing snippet.
Alternatively you can use:
document.querySelectorAll('[id="127"]');
or
document.querySelectorAll('[data-league]')
function getAllDataLeague()
{
var leagues = [];
Array.prototype.forEach.call(document.getElementsByTagName('div'), function (value, index, traversed) {
var attrVal = value.getAttribute('data-league');
if (attrVal) {
leagues.push({'obj': value, 'data-league': attrVal});
}
});
return leagues;
}
var eleFound = getAllDataLeague();
// print result in the html body
document.body.innerHTML += '<p>To access elements: eleFound[index]["data-league"]</p>';
document.body.innerHTML += '<p>To trigger the click event: eleFound[index]["obj"].click()</p>';
document.body.innerHTML += '<p>Elements found:</p>';
for (var i = 0; i < eleFound.length; i++) {
document.body.innerHTML += '<div>data-league:' + eleFound[i]["data-league"] + '</div>';
}
<div class="game-listing-group">
<div class="bold game-listing-name">CS:GO</div><div class="videoPanel vu-channel-tab" id="127" data-channel="https://www-cdn.jtvnw.net/swflibs/TwitchPlayer.swf?channel=m0e_tv" data-league="1284">
<div class="video-thumbnail-con hidden-xs hidden-sm">
<img class="img-responsive video-thumbnail full-width" src="https://static-cdn.jtvnw.net/previews-ttv/live_user_m0e_tv-320x180.jpg">
</div>
<div class="videoPanelTextBg">
<p class="indexVideoPanelTitle">Dragon lore giveaway NOW</p>
<span class="indexVideoPanelGoldCount vu-league-gold pull-right" style="display: none;"></span>
</div>
<div class="indexVideoPanelGoldCount video-upcoming">
<i class="glyphicon glyphicon-time"></i>
<span class="vu-league-start"></span>
</div>
</div><div class="videoPanel vu-channel-tab" id="127" data-channel="https://www-cdn.jtvnw.net/swflibs/TwitchPlayer.swf?channel=freakazoid_tv" data-league="1296">
<div class="video-thumbnail-con hidden-xs hidden-sm videoPanelTextBgActive">
<img class="img-responsive video-thumbnail full-width" src="https://static-cdn.jtvnw.net/previews-ttv/live_user_freakazoid_tv-320x180.jpg">
</div>
<div class="videoPanelTextBg">
<p class="indexVideoPanelTitle">BIRTHDAY IN 24HOURS! #c9freakazoid</p>
<span class="indexVideoPanelGoldCount vu-league-gold pull-right" style="display: none;"></span>
</div>
<div class="indexVideoPanelGoldCount video-upcoming">
<i class="glyphicon glyphicon-time"></i>
<span class="vu-league-start"></span>
</div>
</div><div class="videoPanel vu-channel-tab" id="127" data-channel="https://www-cdn.jtvnw.net/swflibs/TwitchPlayer.swf?channel=gripex90" data-league="1301">
<div class="video-thumbnail-con hidden-xs hidden-sm">
<img class="img-responsive video-thumbnail full-width" src="https://static-cdn.jtvnw.net/previews-ttv/live_user_gripex90-320x180.jpg">
</div>
<div class="videoPanelTextBg">
<p class="indexVideoPanelTitle">Gripex - Most dedicated Lee sin! Top 50 Challenger</p>
<span class="indexVideoPanelGoldCount vu-league-gold pull-right" style="display: none;"></span>
</div>
<div class="indexVideoPanelGoldCount video-upcoming">
<i class="glyphicon glyphicon-time"></i>
<span class="vu-league-start"></span>
</div>
</div><div class="videoPanel vu-channel-tab" id="127" data-channel="https://www-cdn.jtvnw.net/swflibs/TwitchPlayer.swf?channel=phantoml0rd" data-league="1346">
<div class="video-thumbnail-con hidden-xs hidden-sm">
<img class="img-responsive video-thumbnail full-width" src="https://static-cdn.jtvnw.net/previews-ttv/live_user_phantoml0rd-320x180.jpg">
</div>
<div class="videoPanelTextBg">
<p class="indexVideoPanelTitle">Level 400 Gambler LOL - Cycled over 3,000,000 in s</p>
<span class="indexVideoPanelGoldCount vu-league-gold pull-right" style="display: none;"></span>
</div>
<div class="indexVideoPanelGoldCount video-upcoming">
<i class="glyphicon glyphicon-time"></i>
<span class="vu-league-start"></span>
</div>
</div></div>

Angular, how to set class to the clicked list item and remove from others li items?

I have this list where I'm trying to set class to the clicked list item (but class should be set only for clicked item).
I tried to do by this way but without luck:
<a href=""
ng-ctrl="ThemesCtrl"
ng-click="checkThemeContent(theme);"
ng-repeat="theme in themes"
ng-hide="theme[filterProp] !== filterValue"
class="list-group-item">
<b>{{theme.name}}</b>
<span class="themesListIcons">
<i class="fa fa-check-square-o"></i> {{theme.avg_score}}
<i class="fa fa-comment-o"></i> {{theme.count_of_cards}}
</span>
</a>
How can i do it please in the right way?
Thanks for any help.
Great.
Sorry because I have not plunker account, so I must explain you on theoretical way.
George, you have to create function in controller that selects your item, for an example, in your case:
$scope.checkThemeContent = function (name) {
$scope.selectedName = name;
}
After this step, you must only call this function in your HTML like this:
<a href=""
ng-class="{selected:theme.name==selectedName}"
ng-click="checkThemeContent(theme.name)"
ng-repeat="theme in themes"
class="list-group-item">
<b>{{theme.name}}</b>
<span class="themesListIcons">
<i class="fa fa-check-square-o"></i> {{theme.avg_score}}
<i class="fa fa-comment-o"></i> {{theme.count_of_cards}}
</span>
</a>
Please take look on ng-click part. You have to pass some property ('theme.name') of item (in your case 'theme') from list ('themes'), because you can't compare objects.
In ng-class you must also pass the same property as in ng-click.
Hope it helps.
you need change the syntax ng-class directive
<a href=""
ng-class="{'selected':$index==selectedIndex}"
ng-ctrl="ThemesCtrl"
ng-click="checkThemeContent(theme);currentTheme=theme"
ng-repeat="theme in themes"
ng-hide="theme[filterProp] !== filterValue"
class="list-group-item">
<b>{{theme.name}}</b>
<span class="themesListIcons">
<i class="fa fa-check-square-o"></i> {{theme.avg_score}}
<i class="fa fa-comment-o"></i> {{theme.count_of_cards}}
</span>
</a>
If the 'selectedIndex' is your scope variable that represents your selected item in the controller and the 'selected' is your CSS class, try to change the ng-class part like this:
ng-class="{selected: $index==selectedIndex}"

Categories