I have a rails app that creates a standardized card-box for every db record. It creates a button in each box so there are multiples of the same button class. I want to pass the text card-box to send an AJAX request. So each box has a different variable to send. Currently when I click any button it only passes the last element in the card-text array. I'll include a screenshot to show what I'm talking about. Below is the JavaScript snippet that handles the button click.
let track = document.getElementsByClassName('btn btn-sm btn-outline-dark');
let getPackages = document.getElementsByClassName('card-text');
let tNumber = "";
for(let i=0; i<track.length; i++){
track[i].addEventListener('click', function(){
for (let j=0; j<getPackages.length; j++){
console.log(i);
console.log(j);
if (i == j){
console.log(tNumber = getPackages[j].innerText);
tNumber = getPackages[j].innerText;
console.log(tNumber);
sendRequest(tNumber);
}
}
});
}
Entire card html:
<div class="col-md-4">
<div class="card mb-4 shadow-sm">
<svg class="bg-placeholder-img card-img-top" width="100%" height="225" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice" focusable="false" role="img" aria-label="placeholder: Thumbnail">
<title>Package</title>
<rect width="100%" height="100%" fill="#55595c"></rect>
<image href="https://volumeintegration.com/wp-content/uploads/PackageIcon.png" width="100%" height="225"/>
</svg>
<div class="card-body">
<p class="card-text">
<%= package.tracking %>
</p>
<div class="d-flex justify-content-between alight-items-center">
<div class="btn-group">
<button type="button" class="btn btn-sm btn-outline-dark" id="trackButton">Track</button>
<%= link_to 'Delete', package, method: :delete, data: { confirm: 'Are you sure?' }, class: "btn btn-sm btn-danger btn-outline-dark"%>
</div>
</div>
</div>
</div>
</div>
I figured it out I used the .closest() method then got the child
let track = document.getElementsByClassName('btn btn-sm btn-outline-dark');
let tNumber = "";
for(let i=0; i<track.length; i++){
track[i].addEventListener('click', function(){
let parent = track[i].closest('.card-body');
tNumber = parent.getElementsByClassName('card-text')[0].innerText;
console.log(tNumber);
sendRequest(tNumber);
});
}
Related
I am using bootstrap icons and javascript and I would like to know how can I change the icon in an animated way?
toClipboard: function (refElementName, sender) {
let btn = $(sender);
let btnIcon = btn.find("i")
$("[name='" + refElementName + "']").select()
document.execCommand("copy")
btn.fadeOut(400)
btnIcon.attr("class", "bi bi-clipboard-check")
btn.delay(400)
btnIcon.attr("class", "bi bi-clipboard")
}
html
<div class="col position-relative">
<textarea name="textObs" rows="5" class="form-control" placeholder="Observações"></textarea>
<a class="btn btn-primary" id="copy" onclick="toClipboard('textObs', this)"><i class="bi bi-clipboard"></i></a>
</div>
I tried this, but it doesn't work as expected ... the animation happens but in the end the button is without icon
https://jsfiddle.net/r06s5ctn/
Is this what you're looking for?
On click, fade out icon, then set a timeout to change the icon class once it's faded out, then fade the icon back in.
function toClipboard(refElementName, sender) {
let btn = $(sender);
let btnIcon = btn.find("i")
$("[name='" + refElementName + "']").select()
document.execCommand("copy")
btn.fadeOut(400)
setTimeout(function() {
btnIcon.removeClass("bi-clipboard").addClass("bi-clipboard-check")
}, 400)
btn.fadeIn(400)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.4.1/font/bootstrap-icons.min.css">
<div class="col position-relative">
<textarea name="textObs" rows="5" class="form-control" placeholder="Observações"></textarea>
<a class="btn btn-primary" id="copy" onclick="toClipboard('textObs', this)"><i class="bi bi-clipboard"></i></a>
</div>
Alloha !
To change de icon of the button, you just need to use .html from jquery like this :
btn.fadeOut(400)
btnIcon.html("<i class="bi bi-clipboard-check"></i>")
btn.delay(400)
btnIcon.attr("<i class="bi bi-clipboard"></i>")
Have a good day!
index.html
<div class="row">
<div class="col-md-12">
<div class="card shadow-lg">
<div class="card-body" id="div1">
<div class="row">
<div class="col-md-6">
<h5 class="card-title"><i class="fa fa-user-circle" aria-hidden="true"></i>
{{datas.user_id|capfirst}}</h5>
</div>
<div class=" col-md-6 text-right" >
{% if request.user.id == datas.user_id_id %}
<button class="btn btn-sm btn-outline-primary" onclick="load_post(this, '{{datas.id}}')" id="edit">
Edit</button>
{% endif %}
</div>
</div>
<div id="msg">
<hr>
{{datas.post}}
<hr>
</div>
<div id="one">
</div>
<div class="row">
<div class="col-md-4">
<i class="fa fa-thumbs-up" value="0" onclick="count(this, '{{datas.id}}')" id="odd"></i><span id="add"></span>
</div>
<div class="col-md-4"></div>
<div class="col-md-4 text-right">{{datas.post_date}}</div>
</div>
</div>
</div>
</div>
inbox.js
function load_post(id, data)
{
const one = id.closest(".card-body");
one.querySelector('#msg').style.display = 'none';
fetch(`/network/${data}`)
.then(res => res.json())
.then(out => {
const element = document.createElement("textarea");
element.classList.add("form-control");
element.rows = 2;
element.id = "body";
element.innerHTML = out["post"];
one.querySelector('#one').appendChild(element);
const li = document.createElement("br");
one.querySelector('#one').appendChild(li);
const button = document.createElement("button");
button.className = "btn btn-sm btn-success"
//button.classList.add("btn"," btn-primary");
button.id="sucees";
button.innerHTML = "SUBMIT";
button.addEventListener('click',() => edited(id, out["id"]));
one.querySelector('#one').appendChild(button);
})
}
function edited(id, data)
{
var two = document.querySelector('#body').value;
fetch(`/network/${data}`,{
method : 'POST',
body : JSON.stringify({
post : two
})
})
.then(res=>res.json())
.then(out=>console.log(out))
}
views.py
#csrf_exempt
def edit_post(request, post_id):
try:
post = posts.objects.get(user_id=request.user, pk=post_id)
except posts.DoesNotExist:
return JsonResponse({"Out":"Data not found"}, status=404)
if request.method == 'GET':
return JsonResponse(post.serialize())
if request.method == 'POST':
data = json.loads(request.body)
upt = data.get("post","")
post.post = data["post"]
post.save()
print(post)
return redirect(request.META['HTTP_REFERER'])
here i am sharing my index.html, inbox.js and views.py files. The user will post the data and after that they will edit thier correspoding post and after that the entire page will not reload it. when I click the submit button, the div element cannot be modified but data posted in the database. after that, I refresh the page the original posted that will appear in the div element. I need help in this regard.
I am building a simple todo list, but I am having a bit of trouble for about 2 weeks now trying to solve this problem I have with the loops. Can someone explain what I am doing wrong please? I want to check each item off one at a time, I'm currently using for loops to iterate thru each item. The problem I have been facing is that the loops cause all items to be checked off at the same time. I have tried simple if else statements, switches, while loops, nested for loops. Any advice would be greatly appreciated. Code below.
Javascript
const check = document.querySelectorAll('.check');
const uncheck = document.querySelectorAll('.uncheck');
const li = document.querySelectorAll('li');
function checkItem(event) {
for(let i = 0; i < li.length && i < check.length && i < uncheck.length; i++) {
switch(event.target.classList.contains('far')) {
case true:
li[i].style.textDecoration = 'line-through'
check[i].style.display = 'block';
uncheck[i].classList.toggle('check');
break;
default:
li[i].style.textDecoration = 'none';
check[i].style.display = 'none';
uncheck[i].classList.toggle('uncheck');
}
}
}
EJS
<body onclick="checkItem(event)">
<div id="wrapper">
<div id="interface">
<header>
<i class="fas fa-sync-alt" id="delAllBtn"></i>
<div id="datebox">
<p id="time">4:20pm</p>
<br>
<p id="date">September 24, 2020</p>
</div>
</header>
<div id="userInput">
<form action="/list" method="POST">
<input type="text" placeholder="Enter item.." name="item" autofocus required>
<button type="submit" id="addBtn"><i class="fas fa-plus-circle"></i></button>
</form>
</div>
<main>
<ul id="list">
<% for(let i = 0; i < list.length; i++) {%>
<li class="list-item">
<div class="checkbox">
<i class="far fa-circle uncheck"></i>
<i class="fas fa-check-circle check"></i>
</div>
<span><%= list[i].item %></span>
<div id="buttons">
<button type="submit" data-update="<%= list[i]._id %>" class="saveBtn"><i class="fas fa-edit"></i></button>
<button type="button" data-update="<%= list[i]._id %>" class="editBtn"><i class="fas fa-edit"></i></button>
<button type="button" data-delete="<%= list[i]._id %>" class="delItemBtn"><i class="fas fa-trash-alt"></i></button>
</div>
</li>
<% } %>
</ul>
</main>
<footer>
<p>Footer</p>
</footer>
</div>
</div>
<script src="javascripts/main.js"></script>
</body>
It seems like you have answered your own question here, all items are being checked off because you are using a for loop. Try using e.target after defining a function that you want to call after a click event. This way you will only change the individual item that you click on:
function checkItem(el) {
if (el.classList.contains('uncheck')) {
el.classList.add("check");
el.classList.remove("uncheck");
}
}
document.querySelector('#list').addEventListener('click', (e) => {
checkItem(e.target);
});
You are evaluating event.target on every iteration of the for loop.
You should do it based on the current element of the iteration so that each element gets updated based on its own properties.
I can remove any item of array unless the last one. I also use angularjs to show information in the view. I don´t know what is happening with the last item of this array. Please, anyone can help me?
Here is HTML:
<div class="row">
<div class="col-md-12">
<h4><strong>Documento {{index + 1}} de {{documentos.length}}:</strong> {{documentos[index].nome}}</h4>
<iframe style="background: #ccc;" ng-show="exibirPreview" frameborder="0" ng-src="{{versaoLink}}" width="100%" height="300px"></iframe>
<div class="alert alert-warning" ng-hide="exibirPreview">
#Html.Raw(Resources.Dialog.SignDocuments.TypeDocumentCanNotPreDisplayed)
</div>
<hr />
<div class="pull-right btn-row" ng-show="documentos.length > 1">
<button class="btn btn-default" type="button" ng-click="RemoveDoc(index)"><i class="fa fa-fw fa-times"></i> #Resources.Common.RemoveDocList</button>
</div>
</div>
</div>
Here is js/angularjs
$scope.documentos = [
{nome:"document1", chave: "8F65579E3737706F", extensao:".pdf"},
{nome:"document2", chave: "8F65579E3730007F", extensao:".pdf"},
{nome:"document3", chave: "8545579E3737706F", extensao:".pdf"},
{nome:"document4", chave: "8555579E3730007F", extensao:".pdf"}
]
$scope.ViewLink = function () {
var versao = $scope.documentos[$scope.index];
$scope.exibirPreview = versao.extensao == ".pdf" || versao.extensao == ".txt";
if (!$scope.exibirPreview) {
$scope.versaoLink = '';
} else {
$scope.versaoLink = '/Documento/VersaoView?chave=' + versao.chave;
}
};
$scope.ViewLink();
$scope.RemoveDoc = function (index) {
$scope.documentos.splice(index, 1);
$scope.ViewLink();
};
Or Plunker
In your HTML you are preventing the deletion of the last element:
<div class="pull-right btn-row" ng-show="documentos.length > 1">
<!-- -->
</div>
documentos.length > 1 means "hide when it reaches one item in the array".
It should be documentos.length == 0.
It's either this or your index value starts from 1 and not from 0.
The simplest solution would be to change your remove function to take in the document instead of the index. Try this:
$scope.RemoveDoc = function(document) {
var index = $scope.documents.indexOf(document);
$scope.documents.splice(index, 1);
}
in view:
<button class="btn" type="button" ng-click="RemoveDoc(document)">Delete</button>
I am making an app, the user can either select an item or use their camera to get the qr code which translates into an item's ID.
The problem is that I think some JQuery is messing with my scope from working properly.
I have to get the QR code by listening to an innerHtml change, once it changes (DOMSubtreeModified) the following occurs.
var index = 0;
$('#result').one('DOMSubtreeModified', function(e) {
var code = "";
if (e.target.innerHTML.length > 0) {
code = e.target.innerHTML;
$scope.ToRun(code);
}
});
$scope.ToRun = function(code) {
for (i = 0; i < $scope.plantList.length; i++) {
if ($scope.plantList[i].plantcode == code) {
index = i;
break;
}
}
$scope.currentPlant = $scope.plantList[index];
$scope.plantDetails = false;
$scope.searchDetails = true;
}
For some reason the following does not have any effect on my ng-classes. As when an item is selected I hide the input dialogs, and show the result one.
$scope.plantDetails = false;
$scope.searchDetails = true;
But when a user selects the item manually it works just perfectly. (the items have an ng-click on it)
$scope.viewPlant = function(plant) {
$scope.currentPlant = plant
$scope.plantDetails = false;
$scope.searchDetails = true;
};
And the above works fine, with the ng-click. So why won't my new function that listens for an innerHtml change work?
HTML snippet
<div ng-class="{ 'hidden': searchDetails }">
<!-- CHOOSE INPUT TYPE -->
<div class="form-group" style="margin-bottom:0px">
<div class="btn-group btn-group-justified">
<div class="btn-group">
<button type="button" class="btn btn-default" ng-click="digits = false; qr = true">Plant Code</button>
</div>
<div class="btn-group">
<button type="button" class="btn btn-default" ng-click="digits = true; qr = false">QR Code</button>
</div>
</div>
</div>
<br />
<!-- QR CODE INPUT -->
<div ng-class="{ 'hidden': qr }">
<img id="blah" src="./images/placeholder.png" />
<span class="btn btn-default btn-file">
<i class="glyphicon glyphicon-camera"></i>
<input type="file" onchange="readURL(this);handleFiles(this.files)">
</span>
<div id="result">xxxxxx</div>
<canvas id="qr-canvas" width="800" height="600"></canvas>
</div>
<!-- MANUAL SELECTION INPUT -->
<div ng-class="{ 'hidden': digits }">
<input ng-model="search.$" style="width:100%; font-size:30px; text-align:center" placeholder="Plant Code" />
<div style="overflow: auto; max-height:250px">
<table class="table table-striped" style="background-color:lightblue">
<tr ng-repeat="plant in plantList | filter:search" ng-click="viewPlant(plant)" style="cursor:pointer">
<th>{{plant.name}}</th>
<th>{{plant.plantcode}}</th>
</tr>
</table>
</div>
</div>
<div>
</div>
</div>
<div ng-class="{ 'hidden': plantDetails }">
// results - this should appear when one of the above is entered.
// works for manual selection, but not qr code
</div>
Just confused on why my QR input will not fire off the change-class options to hide the searchDetails div and show the plantDetails div
EDIT: Doing a small test, it seems that my class variables are indeed not updating at all. I just put the values at the bottom of my page and they do not update when hitting the block of:
$scope.plantDetails = false;
$scope.searchDetails = true;
You need to let Angular know about the change. Add this at the end of your method and let me know if it works.
$scope.$apply();