Vue recursive component causing Bootstrap's collapse to jump - javascript

I have a Vue.js project and I am using Bootstrap's collapse on a recursive component. The collapse works ok but it is jumpy and not smooth, it was suggested online that padding could be the issue, but I removed all styling and the problem still persists.
Edit: I have found out that the problem is being caused by the amount of children in the recursion. If there is 1 child, the animation is still smooth, but if there is more than 1 child, then it will jump over all of the children past the first one.
What is the best way of handling this?
Demo: http://grabilla.com/0a50a-342e7df2-bad8-4c57-9c10-92aad292396f.html
.media-buttons {
margin-top: 5px;
margin-bottom: 20px;
}
.media-comment {
font-size: 15px;
}
.media-heading{
margin-bottom: 5px;
}
<template>
<div class="media">
<img class="mr-3 comments-img" src="http://placekitten.com/100/100" alt="Generic placeholder image">
<div class="media-body">
<h6 class="media-heading">{{firstName + " " + lastName}}<small><i> {{GetDateFromNow(dateTime)}}</i></small></h6>
<div class="media-comment">{{ commentBody }}</div>
<div class="media-buttons">
<i class="far fa-thumbs-up"></i> 32
<i class="fas fa-reply"></i> Reply
<i class="fas fa-trash-alt"></i> Delete
<i :id="`replies-chevron-${mainId}`" class="fas fa-chevron-circle-down rotate"></i> See Replies
</div>
<div :id="`children-${mainId}`" class="row collapse">
<div>
<div>
<tree-comments v-if="children.length && !maxDepthReached"
v-for="child in children"
:id="child.id"
:main-id="mainId"
:req-id="reqId"
:req-index="reqIndex"
:parent-id="child.parentId"
:comment-index="commentIndex"
:user-id="child.userId"
:current-user="child.currentUser"
:first-name="child.firstName"
:last-name="child.lastName"
:date-time="child.dateTime"
:children="child.children || []"
:comment-body="child.commentBody"
:depth="depth + 1" />
</div>
</div>
<hr v-if="parentId == null" />
</div>
</div>
</div>
</template>

Related

Get direct children Elements at any depth

I'm working on a Comments system whose listing has, in its essence, the following format:
body {
font-size: 1.15rem;
}
.comment {
display: flex;
padding: 1rem;
}
.comment .message {
margin: 0 1rem;
}
.comment .replies {
margin-left: 15px;
}
.icon {
-ms-flex-item-align: center;
align-self: center;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
.icon.dots {
cursor: pointer;
height: 1.5rem;
width: 1.5rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.0.2/css/bootstrap.min.css" rel="stylesheet"/>
<div class="row comment" data-author="author1">
<div class="col w-175 message">
#author1 - Lorem ipsum dolor
</div>
<div class="col tools">
<div class="dropdown dropstart">
<svg class="icon dots" data-bs-toggle="dropdown" aria-expanded="false" role="menu">
<use xlink:href="#dots" />
</svg>
<ul class="dropdown-menu">
<li class="follow">
<a href="javascript:void(0)">
<span class="ms-2">Follow</span>
</a>
</li>
<li class="d-none follow">
<a href="javascript:void(0)">
<span class="ms-2">Unfollow</span>
</a>
</li>
</ul>
</div>
</div>
<div class="row replies">
<div class="col">
<div class="row comment" data-author="author2">
<div class="col w-175 message">
#author2 - Reply #1
</div>
<div class="col tools">
<div class="dropdown dropstart">
<svg class="icon dots" data-bs-toggle="dropdown" aria-expanded="false" role="menu">
<use xlink:href="#dots" />
</svg>
<ul class="dropdown-menu">
<li class="follow">
<a href="javascript:void(0)">
<span class="ms-2">Follow</span>
</a>
</li>
<li class="d-none follow">
<a href="javascript:void(0)">
<span class="ms-2">Unfollow</span>
</a>
</li>
</ul>
</div>
</div>
<div class="row replies">
<div class="col">
<div class="row comment" data-author="author1">
<div class="col w-175 message">
#author1 - Reply #2
</div>
<div class="col tools">
<div class="dropdown dropstart">
<svg class="icon dots" data-bs-toggle="dropdown" aria-expanded="false" role="menu">
<use xlink:href="#dots" />
</svg>
<ul class="dropdown-menu">
<li class="follow">
<a href="javascript:void(0)">
<span class="ms-2">Follow</span>
</a>
</li>
<li class="d-none follow">
<a href="javascript:void(0)">
<span class="ms-2">Unfollow</span>
</a>
</li>
</ul>
</div>
</div>
<div class="row replies">
<div class="col">
<div class="row comment" data-author="author2">
<div class="col w-175 message">
#author2 - Reply #3
</div>
<div class="col tools">
<div class="dropdown dropstart">
<svg class="icon dots" data-bs-toggle="dropdown" aria-expanded="false" role="menu">
<use xlink:href="#dots" />
</svg>
<ul class="dropdown-menu">
<li class="follow">
<a href="javascript:void(0)">
<span class="ms-2">Follow</span>
</a>
</li>
<li class="d-none follow">
<a href="javascript:void(0)">
<span class="ms-2">Unfollow</span>
</a>
</li>
</ul>
</div>
</div>
<div class="row replies">
<div class="col">...</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="d-none">
<svg id="dots" viewBox="0 0 24 24">
<path d="M12 16.5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5-1.5-.67-1.5-1.5.67-1.5 1.5-1.5zM10.5 12c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5-.67-1.5-1.5-1.5-1.5.67-1.5 1.5zm0-6c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5-.67-1.5-1.5-1.5-1.5.67-1.5 1.5z"/>
</svg>
</div>
</svg>
As you can see, comments have an identifier data-attribute with authors' usernames and each Comment has a toolbox of sorts, which I've simplified to one action here.
In this simple example, when the User clicks the *Follow entry, it should flip all of Bootstrap's d-none class on .follow and .unfollow on all toolboxes belonging to that author, so it's possible to Follow/Unfollow without reloading the Page.
Initially, I tried:
const username = 'author1'; // This actually comes from the data-attribute read when clicking the link
const $toolbox = $( sprintf( '[data-author="%s"] .tools', username ) );
sprintf() is a 3rd-party component because I honestly don't really like Template Literals
An honest attempt, but when I wrote it I forgot that this would also bring all matching Elements from child selectors. For example, if the variable username above had the value author1, entries on author2 toolbox would also be changed.
Then I've narrowed the selector:
const username = 'author1'; // This actually comes from the data-attribute read when clicking the link
const $comment = $( sprintf( '[data-author="%s"] .tools', username ) );
const $toolbox = $comment.children().find( '.tools' );
While it solved the previous problem, it created a new one: now entries nested within .replies, even those that DID match the data-attribute, would not change.
Then I tried something silly:
const username = 'author1'; // This actually comes from the data-attribute read when clicking the link
const $comments = $( sprintf( '[data-author="%s"]', username ) );
$comments.each( ( _offset, comment ) => {
const $this = $( comment );
const $toolbox = $this.children().find( '.tools' );
$toolbox.find( 'li.follow' )
.addClass( 'd-none' )
.siblings( 'li.unfollow' )
.removeClass( 'd-none' );
});
And it worked perfectly, but I'm executing the same instructions over and over and over again N times as the data-attribute matches.
How could I accomplish that more efficiently?
[EDIT]
In this very minimalist scenario, the Child Combinator Selector (>) does the trick, however, in the real layout made with Bootstrap Grid, it doesn't. For reference, this is an example of the whole CSS Path:
div#comment_1212130616.comment.gx-0 div.row.gx-3.profile div.col.align-self-center div.row div.col-2.d-flex.flex-row.justify-content-end.tools

how to stop iterating my like button inside my post

I am working on a post system with likes where the user can toggle the post`s like I have done everything correctly except the last step, the problem inside my v-for loop I fetch likes table from post-like relation(many to many since likes table has user_id and post_id) but it is iterating my button even when I add a condition look here-> duplicated like button, I have tried many things v-if, v-show I think the problem is with my algorithm I hope someone can fix that for me thanks.
<div class="panel panel-white post panel-shadow" v-for="post in posts" >
<div class="post-heading">
<div class="pull-left image">
<img v-bind:src="'img/profile/' + post.user.photo" class="img-circle avatar" alt="user profile image">
</div>
<div class="pull-left meta">
<div class="title h5">
<b>{{post.user.name}} </b>
made a post.
</div>
<h6 class="text-muted time">{{post.created_at | hour}}</h6>
</div>
</div>
<div class="post-description">
<p>{{post.content}}</p>
<div class="stats">
<button class="btn btn-default stat-item" #click.prevent="addLike(post.id)">
<i class="fa fa-thumbs-o-up" aria-hidden="false" style="color: blue" v-for="(like) in post.likes" v-bind:style="like.user_id===id && like.post_id===post.id?'color: blue;':'color: gray;'" > Like {{post.likes.length}}
</i> <!-- here is the duplicate problem-->
</button>
<a class="btn btn-default stat-item" #click.prevent>
<i class="fa fa-reply-all"> {{post.comments.length}}</i> Comments
</a>
</div>
</div>
<comment-input :post="post" :userId="id" :userPhoto="userPhoto"></comment-input>
<ul class="comments-list" v-for="comment in post.comments?post.comments:''">
<comments :comment="comment" :userId="id" :userPhoto="userPhoto"></comments>
</ul>
</div>
<hr>
</div>
</div>
Don't loop through the button element, try to use a method likedBythisUser to check if the current user liked the post in order to bind it to the button style :
methods:{
likedBythisUser(post,id){
return post.likes.find(like=>{
return like.user_id===id && like.post_id===post.id;
}) // return a boolean value
}
}
template :
<button class="btn btn-default stat-item" #click.prevent="addLike(post.id)">
<i class="fa fa-thumbs-o-up" aria-hidden="false" style="color: blue" bind:style="likedBythisUser(post,id)?'color: blue;':'color: gray;'" > Like {{post.likes.length}}
</i> <!-- here is the duplicate problem-->
</button>

jQuery toggle for multiple elements

Hello I am trying to make a toggle for multiple elements in jQuery but it not working the plus button when click does not slide toggle the element please help here is the link
HTML
<!-post begin-- post 4 >
<div class="col-xs-12 post-card">
<div class="col-xs-3">
</div>
<div class="col-xs-7 post-start" >
hello this is a test caption
</div>
<div class="col-xs-2">
<button class="fa fa-plus plus-icon post-btn11" type="button">+</button>
</div>
<div class="collapse col-xs-12" style="font-size: 16px; color:#646464;">
<i class="fa fa-comments comment2"></i> <span>10 comments</span><span class="margin-left-10"><i class="fa fa-heart"></i>
10 likes</span>
</div>
</div>
<!-post end for post 4-->
<!-post begin-- post 5 >
<div class="col-xs-12 post-card">
<div class="col-xs-3">
</div>
<div class="col-xs-7 post-start" >
This is a test comment
</div>
<div class="col-xs-2">
<button class="fa fa-plus plus-icon post-btn11" type="button" >+</button>
</div>
<div id="post-info" class="collapse col-xs-12" style="font-size: 16px; color:#646464;">
<i class="fa fa-comments comment2"></i> <span>11 comments</span><span class="margin-left-10"><i class="fa fa-heart"></i>
13 likes</span>
</div>
</div>
<!-post end for post 5-->
CSS
.post-card {
background-color: #f4f4f4;
padding: 10px;
border: 1px solid #eee;
margin-top: 10px;
}
.fa {
float: right;
}
.post.info {
display: none;
}
jQuery
$(document).ready(function(){
$(".post-btn11").click(function(){
$(this).siblings(".post-info").slideToggle();
});
});
Totally wrong selector post-info is an id not a class, then post-info not siblings of post-btn11 but it's parent siblings, so the selector should like this:
$(".post-btn11").click(function(){
$(this).parent().siblings('#post-info').slideToggle();
});
But better use class because it should repeat and not unique. Also add this class to first one too.
$(".post-btn11").click(function(){
$(this).parent().siblings('.post-info').slideToggle();
});
JSFiddle
Here is the correction to your code. I have also updated your fiddle so go check it out. You had a problem with giving your post-info element an ID. In corrected version I simply add post-info as a class name. And also the .sublings() function will not work, since you applied it to your button element and it will not find any nested elements in it. So I needed to find a button's parent element first to get on the "same level" with your next post-info element and then simply use .next() function
$(document).ready(function(){
$(".post-btn11").click(function(){
$(this).parent(".col-xs-2").next(".post-info").slideToggle();
});
});
.post-card {
background-color: #f4f4f4;
padding: 10px;
border: 1px solid #eee;
margin-top: 10px;
}
.fa {
float: right;
}
.post.info {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-post end for post 3-->
<!-post begin-- post 4 >
<div class="col-xs-12 post-card">
<div class="col-xs-3">
</div>
<div class="col-xs-7 post-start" >
hello this is a test caption
</div>
<div class="col-xs-2">
<button class="fa fa-plus plus-icon post-btn11" type="button">+</button>
</div>
<div class="collapse col-xs-12 post-info" style="font-size: 16px; color:#646464;">
<i class="fa fa-comments comment2"></i> <span>10 comments</span><span class="margin-left-10"><i class="fa fa-heart"></i>
10 likes</span>
</div>
</div>
<!-post end for post 4-->
<!-post begin-- post 5 >
<div class="col-xs-12 post-card">
<div class="col-xs-3">
</div>
<div class="col-xs-7 post-start" >
This is a test comment
</div>
<div class="col-xs-2">
<button class="fa fa-plus plus-icon post-btn11" type="button" >+</button>
</div>
<div class="collapse col-xs-12 post-info" style="font-size: 16px; color:#646464;">
<i class="fa fa-comments comment2"></i> <span>11 comments</span><span class="margin-left-10"><i class="fa fa-heart"></i>
13 likes</span>
</div>
</div>
<!-post end for post 5-->

Stacking nested Divs like accordion (without plugin) for dynamic content and varying nested structure

update: made more progress to show all child elements- https://jsfiddle.net/zigzag/jstuq9ok/11/ Now just need to address this for subsecond area to display also. If you now click on Lily, shen and another shen show up but if you now click on shen to drill down, it just hides it. thoughts?
I want to be clear that in this case data is coming from a source system, so we won't want/like to hard code things. That said, here is the structure we want...
Adam
Lily
2.1 Sen
2.1.1 Tank
2.2 Another Sen
Justin
Thanks to online contributing community, I got that far . progress: https://jsfiddle.net/zigzag/jstuq9ok/10/
You will notice that when you click on Lily, you will only see Sen and not the Another Sen. I tried placing markup with 2.2 ahead of 2.1.1 and vice versa but neither worked. I'm sure i'm missing something in jQuery DOM traversal.
$('.menu-toggle').click(function(e) {
e.preventDefault(); // prevent <a> to redirect to the top of the page
$('.row:not(.sub):not(.subsecond)').not($('.sub').prev('.row')).not($('.subsecond').prev('.row')).not($(this).closest('.row')).find('span.glyphicon-menu-up').toggleClass('glyphicon-menu-down glyphicon-menu-up');
$(this).find('span').toggleClass('glyphicon-menu-down glyphicon-menu-up');
var Nextrow = $(this).closest('.row').next('.row'); // get the next row
if (Nextrow.hasClass('sub')) { // if next row has class sub
$('.sub').not(Nextrow).hide(); // hide all sub but not this one
Nextrow.slideToggle(function() {
if ($(this).is(':hidden')) {
$('.subsecond').hide();
$('span.glyphicon-menu-up').toggleClass('glyphicon-menu-down glyphicon-menu-up');
}
}); // toggle this sub row
return false;
}
if (Nextrow.hasClass('subsecond')) { // if next row has class subsecond
$('.subsecond').not(Nextrow).hide(); // hide all subsecond but not this one
Nextrow.slideToggle(); // toggle this subsecond row
return false;
}
});
.pageArea {
background-color: black;
}
.orgChart {
margin: 10px;
padding: 10px;
background-color: gray;
font-family: segoe UI;
color: white;
}
.img_title {
vertical-align: middle;
}
.empDetails {
font-size: 14px;
color: white;
}
a:link,
a:visited,
a:hover,
a:focus,
a:active {
color: white;
}
.toggle_class {
text-align: right;
}
.sub {
display: none;
margin-left: 20px;
}
.subsecond {
display: none;
margin-left: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<div class="container pageArea">
<div class="row orgChart">
<div class="col-sm-2 img_tile">
<img src="http://via.placeholder.com/100x100">
</div>
<div class="col-sm-8 empDetails">
<h4>
Adam
</h4>
<p>
Director
</p>
<p>
Ensure quality and timely delivery through resource and time management.
</p>
</div>
</div>
<div class="row orgChart">
<div class="col-sm-2 img_tile">
<img src="http://via.placeholder.com/100x100">
</div>
<div class="col-sm-8 empDetails">
<h4>
Lily
</h4>
<p>
Project Manager
</p>
<p>
Ensure quality and timely delivery through resource and time management.
</p>
</div>
<div class="col-sm-2 toggle_button">
<a class="menu-toggle" href="#">
<span class="glyphicon glyphicon-menu-down"></span>
</a>
</div>
</div>
<div class="row orgChart sub">
<div class="col-sm-2 img_tile">
<img src="http://via.placeholder.com/100x100">
</div>
<div class="col-sm-8 empDetails">
<h4>
Sen
</h4>
<p>
Team Lead
</p>
<p>
Ensure quality and timely delivery through resource and time management.
</p>
</div>
<div class="col-sm-2 toggle_button">
<a class="menu-toggle" href="#">
<span class="glyphicon glyphicon-menu-down"></span>
</a>
</div>
</div>
<div class="row orgChart sub">
<div class="col-sm-2 img_tile">
<img src="http://via.placeholder.com/100x100">
</div>
<div class="col-sm-8 empDetails">
<h4>
Other Sen
</h4>
<p>
Team Lead
</p>
<p>
Ensure quality and timely delivery through resource and time management.
</p>
</div>
</div>
<div class="row orgChart subsecond">
<div class="col-sm-2 img_tile">
<img src="http://via.placeholder.com/100x100">
</div>
<div class="col-sm-8 empDetails">
<h4>
Tank
</h4>
<p>
Designer
</p>
<p>
Ensure quality and timely delivery through resource and time management.
</p>
</div>
<div class="col-sm-2 toggle_button">
<a class="menu-toggle" href="#">
<span class="glyphicon glyphicon-menu-down"></span>
</a>
</div>
</div>
<div class="row orgChart">
<div class="col-sm-2 img_tile">
<img src="http://via.placeholder.com/100x100">
</div>
<div class="col-sm-8 empDetails">
<h4>
Justin
</h4>
<p>
Director
</p>
<p>
Ensure quality and timely delivery through resource and time management.
</p>
</div>
<div class="col-sm-2 toggle_button">
<a class="menu-toggle" href="#">
<span class="glyphicon glyphicon-menu-down"></span>
</a>
</div>
</div>
</div>
Your are changing only "next" row. ( $(this).closest('.row').next('.row'); ) I guess you wan`t to target next AND all the siblings rows.
As Temani pointet in his comment would be easier if you make nested divs grouping the rows that should open together.
$('span').click(function(){
$('.sublevel').addClass('toClose');/*close all*/
$(this).next('.sublevel').removeClass('toClose');/*but not the one after this toggler*/
$(this).next('.sublevel').parents('.sublevel').removeClass('toClose');/*and also keep open it's ancestors*/
$('.sublevel.toClose').hide();/*hide ones to close*/
$('.sublevel:not(.toClose)').show();/*show the others*/
$('.sublevel.toClose').removeClass('toClose');/*tidy up*/
}
);
div{padding-left:2rem;}
.sublevel{display:none;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
1 Adam
</div>
<div>
2 Lily<span>[toggler]</span>
<div class="sublevel">
<div>
2.1 Sen<span>[toggler]</span>
<div class="sublevel">
2.1.1 Tank
</div>
</div>
<div>
2.2 Another Sen
</div>
</div>
</div>
<div>
3 Justin<span>[toggler]</span>
<div class="sublevel">
<div>
3.1 someone
</div>
<div>
3.2 someother<span>[toggler]</span>
<div class="sublevel">
3.1.1 this one
</div>
</div>
<div>
3.3 someother 2
</div>
<div>
3.4 someother 3
</div>
</div>
</div>

Why bootstrap collapse is automatically collapsing(shrinking) the element?

Hi I am using the AngularJs with HTML to have dynamic content.
I am also making use of bootstrap for collapse feature.
I have created question answer page which is similar to Stackoverflow.
When page loads, it shows up the all questions and when I click on any particular question then it expands that question and with answers but answers are again collpased and there is one anchor tag to expand all answers.
My problem is that when I am clicking on answer's anchor tag the answers are showing up for few seconds and again it is collapsing automatically.
Below is my code:
<div style="" class="row ans-txt left-margin-img">
{{ answer.a_list.as.length }} ANSWERS
<a data-target=".demo" data-parent="" data-toggle="collapse">
<img src="assets/svg/downarrow_active.svg" class="img-responsive" style="height: 24px; width: 24px; display: inline; float: right; right: 18px; position: absolute;"></img>
</a>
</div>
<div class="row " style="padding-top:14px;height:auto;" ng-repeat-start="(key, value) in answer.a_list.as">
<div class="col-xs-2 col-sm-1 col-md-1 content-padding2 collapse demo">
<div>
<button type="submit" class="searchStyle bgStyle btn-Style vote-btn-border" ng-click="vote(value.raw, 1)">
<i class="glyphicon glyphicon-chevron-up"></i>
<img src="assets/svg/uparrow.svg" class="img-responsive" style="height:24px;width:24px;"></img>
</button>
</div>
<div><button type="text" class="searchStyle bgStyle btn-txt" disabled>{{ value.raw.netvotes }}</button></div>
<div>
<button type="submit" class="searchStyle bgStyle btn-Style vote-btn-border" ng-click="vote(value.raw, 0)">
<i class="glyphicon glyphicon-chevron-down"></i>
<img src="assets/svg/downarrow_active.svg" class="img-responsive" style="height:24px;width:24px;"></img>
</button>
</div>
</div>
<div class="col-xs-11 col-sm-10 col-md-11 padding-rightPane collapse demo">
<p class="ex2">{{ value.raw.content }}</p>
<p class="bottom-margin14">
<div class="askedBy-txt">
Answered by {{ value.raw.handle ? value.raw.handle : value.raw.email }} on {{ value.raw.created * 1000 | date:'MMMM dd, yyyy'}}
</div>
<img src="assets/svg/tick_icon.svg" style="height:20px;width:20px;" class="img-responsive pull-right" title="Best answer"
ng-if="value.select_text === 'Best answer'"></img>
</p>
</div>
</div>
Try to toogle the class .collapse.in. It toogles the css state of collapse element.
<div class="...collapse..." ng-class="{'in': !isCollapsed}">
</div>

Categories