jQuery sort cards with up/down arrows - javascript

I am using Bootstrap cards on a page, where I want those cards to be sorted using up and down arrows. Every card has arrows in its header, to move it up or down in the layout. But of course, when it gets moved to the top, the up arrow should be gone, likewise for when it hits the bottom.
I've tried using if statements in this style:
// Result: Is never executed.
if(!$(card).find('.sort-down')) {}
// Result: Is always executed.
if($(card).not(':has(.sort-down)') {}
Where $(card) is the div containing the complete card that is being moved.
For example, my complete "move down" code block.
$(document).on('click', '.sort-down', function(e) {
e.preventDefault();
let card = $(this).closest('.card');
let sort_id = $(card).attr('data-sort');
let next_sort_id = Number(sort_id) + 1;
$('[data-sort]').each(function(index, value) {
if($(value).attr('data-sort') == next_sort_id) {
$(value).after($(card));
$(value).attr('data-sort', sort_id);
$(card).attr('data-sort', next_sort_id);
// Change buttons
if($(card).attr('data-sort') == sortCount - 1) {
$(card).find('.sort-down').remove();
if(!$(card).find('.sort-up')) {
// Insert up arrow
}
} else {
if(!$(card).find('.sort-up')) {
// Insert up arrow
}
if(!$(card).find('.sort-down')) {
// Insert down arrow
}
}
if($(value).attr('data-sort') == 0) {
$(value).find('.sort-up').remove();
if(!$(value).find('.sort-down')) {
// Insert down arrow
}
} else {
if(!$(value).find('.sort-down')) {
// Insert down arrow
}
if(!$(value).find('.sort-up')) {
// Insert up arrow
}
}
return false;
}
});
});
A little bit of the HTML so you can get an idea what classes/attributes my jQuery is selecting.
<div class="card mb-3" data-sort="0">
<div class="card-header">
Tekstveld
<button type="button" class="btn btn-sm btn-primary float-right mr-1 sort-down"><i data-feather="arrow-down" width="16" height="16"></i></button>
</div>
<div class="card-body p-0">
<div id="editor"></div>
</div>
</div>
As I usually overcomplicate things, and get the result of it not even working, I'm hoping you could help me with either a simple or more advanced working solution of getting this done.

Using data-sort attribute as a sorting index could be more useful for "Global" sorting or filtering. Like when we click on one button to sort them all based on that property value.
But here, a less complicated alternative is to use jQuery default methods like : next(),prev(),closest(),insertAfter(),insertBefore()
$( document ).ready(function() {
setButtons();
$(document).on('click', '.sort-down', function(e) {
var cCard = $(this).closest('.card');
var tCard = cCard.next('.card');
cCard.insertAfter(tCard);
setButtons();
resetSort();
});
$(document).on('click', '.sort-up', function(e) {
var cCard = $(this).closest('.card');
var tCard = cCard.prev('.card');
cCard.insertBefore(tCard);
setButtons();
resetSort();
});
function resetSort(){
var i=0;
$('.card').each(function(){
//$(this).data('sort',i);
$(this).attr("data-sort", i);
i++;
});
}
function setButtons(){
$('button').show();
$('.card:first-child button.sort-up').hide();
$('.card:last-child button.sort-down').hide();
}
function resetSort(){
var i=0;
$('.card').each(function(){
//$(this).data('sort',i);
$(this).attr("data-sort", i);
i++;
});
}
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="card mb-3" data-sort="0">
<div class="card-header">
Tekstveld
<button type="button" class="btn btn-sm btn-primary float-right mr-1 sort-down"><i class="fas fa-caret-down"></i></button>
<button type="button" class="btn btn-sm btn-primary float-right mr-1 sort-up"><i class="fas fa-caret-up"></i></button>
</div>
<div class="card-body p-0">
<div class="editor"></div>
</div>
</div>
<div class="card mb-3" data-sort="1">
<div class="card-header">
Voorbeeld
<button type="button" class="btn btn-sm btn-primary float-right mr-1 sort-down"><i class="fas fa-caret-down"></i></button>
<button type="button" class="btn btn-sm btn-primary float-right mr-1 sort-up"><i class="fas fa-caret-up"></i></button>
</div>
<div class="card-body p-0">
<div class="editor"></div>
</div>
</div>
<div class="card mb-3" data-sort="2">
<div class="card-header">
Lorem ipsum
<button type="button" class="btn btn-sm btn-primary float-right mr-1 sort-down"><i class="fas fa-caret-down"></i></button>
<button type="button" class="btn btn-sm btn-primary float-right mr-1 sort-up"><i class="fas fa-caret-up"></i></button>
</div>
<div class="card-body p-0">
<div class="editor"></div>
</div>
</div>
</div>
[UPDATE] : The setButtons() function would hide the non functional buttons when the div reach the limit.
Notice that I added another function resetSort() to reorder those data-sort values. Just in case, you need it for global sorting.

Related

addEventListener for click is not working

const renderNote = data => {
const postListRef = ref(db, 'Notes/' +data.key);
console.log(data.key)
const newPostRef = push(postListRef);
var status = 'Pending'
var title = 'new note'
var date = '29-4-2022'
var note = 'newly added note'
let card =
`<div id="single-card" class="col-lg-4 col-md-3" data-id=${data.key} ><!--outer layer of single card-->
<div class="card card-body"><!--card body-->
<p class="badge" id="status" style="background-color: rgb(0, 81, 81);">${status}</p>
<span class="side-stick"></span> <!--side-stick color-->
<!-- note title -->
<h5 class="note-title text-truncate w-75 mb-0" >${title}<i class="point fa fa-circle ml-1 font-10"></i></h5><!--fa fa-circle is for the dot dot dot(continuity)-->
<p class="note-date font-12 text-muted mt-0">${date}</p>
<!--note description-->
<div class="note-content">
<p class="note-inner-content text-muted" >${note}<i class="point fa fa-circle ml-1 font-10"></i></p>
</div>
<button class="btn btn-del">Delete${data.key}</button>
<div id="actions" >
</div>
</div>
</div>`
prod.innerHTML += card;
const btnDelete = document.querySelector(`[data-id='${data.key}'] .btn-del`);
console.log(btnDelete);
btnDelete.addEventListener("click",()=>{
console.log('deleting');
});
}
EventListener is not working. But when I print the btnDelete(console.log(btnDelete);) it is printing correctly. But the eventlistener is not workingDoes anybody know what is wrong with the code?
Why donĀ“t you use onclick in HTML?
<button onclick="console.log('deleting');" class="btn btn-del">Delete${data.key}</button>
You can even call a delete Function like that:
HTML:
<button onclick="deleteCard(${data.key})" class="btn btn-del">Delete${data.key}</button>
JavaScript:
function deleteCard(key) {
console.log(`Delete Card with key: ${key}`)
}

I want to show only one input clicked (on vue.js)

I would like to show an input on the click, but being in a for loop I would like to show only the clicked one
<div v-for="(todo, n) in todos">
<i class="fas fa-minus ml-2"></i>
<li class="mt-2 todo">
{{ todo }}
</li>
<li class="button-container">
<button class="ml-1 btn btn-primary rounded-circle btn-sm" v-if="isHidden" v-on:click="isHidden = false"><i
class="THIS-CLICK"></i></button>
<input class="ml-5 border border-primary rounded" v-if="!isHidden" v-model="todo">
<button class="ml-1 btn btn-success rounded-circle btn-sm" v-if="!isHidden" v-on:click="isHidden = true"
#click="modifyTodo(n, todo)"><i class="far fa-save"></i></button>
</li>
</div>
I would like that on clicking on THIS-CLICK, only one input (that of the button clicked) is visible, but being in a for loop I don't know if it can be done
I would suggest to change the structure a bit in your app. You can clean up your code a lot by using v-if inside the button instead of two different buttons.
Also, moving as much javascript out from the markup as possible is a good practice.
I have an example below where this is done.
Regarding your question, you would have to add the key to each todo item.
new Vue({
el: "#app",
data() {
return {
todos: [{
name: 'wash hands',
isHidden: true
},
{
name: 'Stay home',
isHidden: true
}
],
};
},
methods: {
toggleTodo(n, todo) {
const hidden = todo.isHidden;
if (hidden) {
this.modifyTodo(n, todo);
todo.isHidden = false;
} else {
todo.isHidden = true;
}
},
modifyTodo(n, todo) {
//Some logic...
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<div v-for="(todo, n) in todos">
<i class="fas fa-minus ml-2"></i>
<li class="mt-2 todo">
{{ todo.name }}
</li>
<li class="button-container">
<input class="ml-5 border border-primary rounded" v-if="!todo.isHidden" v-model="todo.name">
<button #click="toggleTodo(n, todo)">
<i v-if="todo.isHidden" class="THIS-CLICK">click-this</i>
<i v-else class="far fa-save">save</i>
</button>
</li>
</div>
</div>
If you cannot do this, you could go with adding a new key to data like: hiddenTodos that would be an array of ids/a unique identifier to the todo you selected to hide.
in the template, it would be something like this:
<button #click="hiddenTodos.push(todo)">
...
<div v-if="hiddenTodos.includes(todo)"

passing a string argument into angularjs ng-click function

Not sure how to do this, but I would like to pass a string as an argument into ng-click and then us it as a conditional in the function. so something like this?
<div class="row">
<div class="col-md-12 text-center mb-5">
<div class="btn-group">
<button class="btn btn-primary" ng-click=""><span class="ion-plus-circled mr-2"></span>New</button>
<button class="btn btn-outline-primary" ng-click="filter_emails('inbox')"><span class="ion-archive mr-2"></span>Inbox</button>
<button class="btn btn-outline-primary" ng-click=""><span class="ion-paper-airplane align-middle mr-2"></span>Shielded</button>
</div>
</div>
</div>
and then in my controller:
$scope.filter_emails = function(category) {
if (category == "inbox") {
$scope.grouped = group(inbox($scope.emails));
}
else {
$scope.grouped = group($scope.emails);
}
}
This is not working or I obviously wouldn't be posting the question, so what would the correct approach to this?
If you want to pass a string enclose it within quotes
ng-click="filter_emails('inbox')"

How to create and store value of the progress bars in local storage

First,I have a textarea for tasks and an add button. When the button is clicked it will be stored in the database.All I want to do is to output the task with a progress bar and buttons for increment and decrement.
Does anyone know about bootstrap progress bar?
The progress bar must be incremented and decremented.
and the last value must be the latest value of it when a user came back to that progress bar.
I think jquery and javascript is the best solution for this. But I do not know how :( Can someone please help.
heres the code for the output:
<html>
<head>
<title>
Customize Bootstrap progressbar
</title>
<link href="bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/>
</head>
<body style="margin-top: 100px">
<div class=" col-sm-6 col-sm-offset-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
Customize Bootstrap progressbar
</h3>
</div>
<div class="panel-body" style="padding-top: 50px">
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="0 %" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
</div>
</div>
</div>
<div class="panel-footer">
<div class="btn-group" role="group" aria-label="...">
<button type="button" class="btn btn-default" onclick="progress.increment(10)">
Increment
</button>
<button type="button" class="btn btn-default" onclick="progress.decrement(10)">
Decrement
</button>
<button type="button" class="btn btn-default"onclick="progress.reset()">
Reset
</button>
<button type="button" class="btn btn-default"onclick="progress.complete()">
Complete
</button>
</div>
</div>
</div>
</div>
</body>
<script src="jquery/jquery.min.js">
</script>
<script>
var progress = (function ($) {
var progress = $('.progress'),
progress_bar = $('.progress-bar'),
total_width = progress.width();
function calculatePercentage(increment_by,is_increment) {
var progress_percentage;
if (is_increment == true) {
progress_percentage = Math.round((progress_bar.width() / total_width) * 100 + increment_by) ;
progress_percentage = (progress_percentage > 100) ? 100 : progress_percentage;
} else {
progress_percentage = Math.round((progress_bar.width() / total_width) * 100 - increment_by) ;
progress_percentage = (progress_percentage < 0) ? 0 : progress_percentage;
}
return progress_percentage;
}
return{
increment: function (increment_by) {
var progress_percentage = calculatePercentage(increment_by, true);
progress_bar.css('width',progress_percentage + '%').attr('aria-valuenow', progress_percentage + ' %');
},
decrement: function (decrement_by) {
var progress_percentage = calculatePercentage(decrement_by, false);
progress_bar.css('width',progress_percentage+'%').attr('aria-valuenow', progress_percentage + ' %');
},
reset: function () {
progress_bar.css('width',0 + '%').attr('aria-valuenow', 0 + ' %');
},
complete: function () {
progress_bar.css('width',100 + '%').attr('aria-valuenow', 100 + ' %');
}
};
})( jQuery);
</script>
enter image description here
You need to implement the localStorage as shown below. Also I have converted the code to a jQuery plugin, which will be initialized per widget. All your progressBars are incremented at once as you are using something like progress_bar = $('.progress-bar') which means you are picking up all the progress_bars on the page, not targetting a single progress bar. I have create an attachEvent inside the plugin code, so you do not need to initialize the events from the dom. In general its a good practice to attach events from the script.
I would suggest adding a function createHTML for your HTML creation and call that code in init to create your HTML. You can use the same localStorage to track how many progress bars you have on page, when you refresh the page, and need to recreate the progress bars.
Try using templates for the html, as repeated html is not ideal.
<html>
<head>
<title>
Customize Bootstrap progressbar
</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"> </script>
</head>
<body style="margin-top: 100px">
<div class=" col-sm-6 col-sm-offset-3">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
Customize Bootstrap progressbar
</h3>
</div>
<div id="progress-bar-1" class="progress-bar-widget">
<div class="panel-body" style="padding-top: 50px">
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="0 %" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
</div>
</div>
</div>
<div class="panel-footer">
<div class="btn-group" role="group" aria-label="...">
<button type="button" class="btn btn-default increment">
Increment
</button>
<button type="button" class="btn btn-default decrement" >
Decrement
</button>
<button type="button" class="btn btn-default reset">
Reset
</button>
<button type="button" class="btn btn-default complete">
Complete
</button>
</div>
</div>
</div>
<div id="progress-bar-2" class="progress-bar-widget">
<div class="panel-body" style="padding-top: 50px" class="progress-bar-widget">
<div class="progress">
<div class="progress-bar" role="progressbar" aria-valuenow="0 %" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
</div>
</div>
</div>
<div class="panel-footer">
<div class="btn-group" role="group" aria-label="...">
<button type="button" class="btn btn-default increment" >
Increment
</button>
<button type="button" class="btn btn-default decrement" ">
Decrement
</button>
<button type="button" class="btn btn-default reset">
Reset
</button>
<button type="button" class="btn btn-default complete">
Complete
</button>
</div>
</div>
</div>
</div>
</div>
</body>
<script>
(function ($) {
$.fn.progressBar = function( ) {
var widget = $(this),
progress = $(this).find('.progress'),
progress_bar = $(this).find('.progress-bar'),
total_width = progress.width();
function init() {
var curr_progress = getLocalStorage();
if(curr_progress) {
progress_bar.css('width',curr_progress + '%').attr('aria-valuenow', curr_progress + ' %');
}
attachEvents();
}
function attachEvents() {
widget.find('.increment').on("click", function(){increment(10);});
widget.find('.decrement').on("click", function(){decrement(10);});
widget.find('.reset').on("click", reset);
widget.find('.complete').on("click", complete);
}
function calculatePercentage(increment_by,is_increment) {
var progress_percentage;
if (is_increment == true) {
progress_percentage = Math.round((progress_bar.width() / total_width) * 100 + increment_by) ;
progress_percentage = (progress_percentage > 100) ? 100 : progress_percentage;
} else {
progress_percentage = Math.round((progress_bar.width() / total_width) * 100 - increment_by) ;
progress_percentage = (progress_percentage < 0) ? 0 : progress_percentage;
}
return progress_percentage;
}
function getLocalStorage() {
return localStorage.getItem(widget.attr('id'));
}
function setLocalStorage(val) {
localStorage.setItem(widget.attr('id'), val);
}
function increment(increment_by) {
var progress_percentage = calculatePercentage(increment_by, true);
setLocalStorage(progress_percentage);
progress_bar.css('width',progress_percentage + '%').attr('aria-valuenow', progress_percentage + ' %');
};
function decrement (decrement_by) {
var progress_percentage = calculatePercentage(decrement_by, false);
setLocalStorage(progress_percentage);
progress_bar.css('width',progress_percentage+'%').attr('aria-valuenow', progress_percentage + ' %');
};
function reset () {
setLocalStorage(0);
progress_bar.css('width',0 + '%').attr('aria-valuenow', 0 + ' %');
};
function complete () {
setLocalStorage(100);
progress_bar.css('width',100 + '%').attr('aria-valuenow', 100 + ' %');
}
init();
}
$('.progress-bar-widget').each(function(index, elem){
$(elem).progressBar();
});
})( jQuery);
</script>
Hope it helped! :) All the best!

jQuery loop through .hover( ) function using .each( )

I have a function that changes the border-color of the arrows to match the hover state colour of the buttons. It is working as 3 separate functions but I want to combine them using .each( ) Please tell me where I am going wrong
Vars
var btnArrowPath = '.btn + .btn-arrow';
var btnName = ['btn-infra ','btn-dev ','btn-anal '];
var btnColor = ['#286090','#449d44','#31b0d5'];
var btnRollColor = ['#337ab7','#5cb85c','#5bc0de'];
Function
$('.btn-wrap').each(function(i, value){
$(btnName[value] + '.btn').hover(function(i, value){
$(btnName[value] + btnArrowPath).css('border-top-color',btnColor[value]);
}, function(){
$(btnName[value] + btnArrowPath).css('border-top-color',btnRollColor[value]);
});
});
HTML
<div class="hero-wrap">
<div class="hero-popup">
<div class="row">
<div class="btn-wrap btn-infra">
<button data-trigger="focus" type="button" class="btn btn-primary" data-toggle="popover" title="Infrastructure" data-content="And here's some amazing content. It's very engaging. Right? <a href='#'>Do it!</a>" data-placement="bottom">Infrastructure</button>
<div class="btn-arrow pull-right"></div>
</div>
</div>
<div class="row">
<div class="btn-wrap btn-dev">
<button data-trigger="focus" type="button" class="btn btn-info" data-toggle="popover" title="Development" data-content="And here's some amazing content. It's very engaging. Right? <a href='#'>Do it!</a>" data-placement="top">Development</button>
<div class="btn-arrow pull-right"></div>
</div>
</div>
<div class="row">
<div class="btn-wrap btn-anal">
<button data-trigger="focus" type="button" class="btn btn-success" data-toggle="popover" title="Analysis" data-content="And here's some amazing content. It's very engaging. Right? <a href='#'>Do it!</a>" data-placement="top">Analysis</button>
<div class="btn-arrow pull-right"></div>
</div>
</div>
</div>]
You want to have the hover function for the .btn element inside the .btn-wrap element, which will change the border top color of the next sibling of the hovered element, isn't it?
So
$('.btn-wrap .btn').each(function (i, value) {
$(this).hover(function (e) {
$(this).next('.btn-arrow').css('border-top-color', btnColor[i]);
}, function () {
$(this).next('.btn-arrow').css('border-top-color', btnRollColor[i]);
});
});
Demo: Fiddle
You can do it without the loop like
var $btns = $('.btn-wrap .btn').hover(function (e) {
$(this).next('.btn-arrow').css('border-top-color', btnColor[$btns.index(this)]);
}, function () {
$(this).next('.btn-arrow').css('border-top-color', btnRollColor[$btns.index(this)]);
});
Demo: Fiddle

Categories