Jquery click function not working for all the ids - javascript

I have created a page which contains all the products from the database, and this is dynamic page. Columns are created in loop. Whenever user clicks the product i am fetching the product id which is unique, however this is working only for one product, for next product even if click the function is not triggered. below is the code for reference.
{{ album.product_name }}
Category : {{ album.product_category }}
Price : {{ album.product_price }}
$(document).ready(function() {
$('#k').click(function(){
var a = $('#custId').val();
alert(a)
console.log(a)
});
This is working perfectly fine for the first product only i.e the first product from the loop. for rest the click function is not working. Please help!
Below is the HTML code:
<div class="card-body">
<a href="#" id="k">
<img src="qph.fs.quoracdn.net/…" class="card-img-top" alt="..." height="90px" width="85px" id="k">
</a>
<h5 class="card-title">{{ album.product_name }}</h5>
<p class="card-text">Category : {{ album.product_category }}</p>
<p class="card-text">Price : {{ album.product_price }}</p>
<input type="hidden" id="custId" value={{ album.id }}>
</div>

You cannot have multiple elements with the same ID. Consider adding a common class name to all the target elements, say myClass, then you can bind the desired event using:
$('.myClass').click(function(){
//your code here...
});
Explanation: The browser assumes you only have one element with an id of k (because is should be like so by convention), therefore $("#k") will only target the first element with that ID.

Related

How to get values of input which has the same class as another?

I have a page which renders divs as per the amount of results found in the search feature I have, and then In those divs I have hidden inputs with some information about the found user. One of the hidden inputs is the ID, What I want to happen is when the user clicks on the name label in any one of the divs, I want to save that hidden ID input field value to a variable using jQuery, so that I can use it as to determine what page to go to next by adding it in as a param in a url. I am using jQuery, Twig and PHP.
$(document).ready(function (){
$('.search-result-name').click(function (){
var lawyerId = $('.search-result-id').val();
window.location.href = "/profile?lawyerId="+lawyerId;
})
})
<div class="search-result-info">
<input type="hidden" value="{{ item.id }}" class="search-result-id">
<input type="hidden" value="{{ item.verifiedLicense }}" class="search-result-id" id="verified-license">
<div class="search-result-name">{{ item.firstName }} {{ item.lastName }}</div>
</div>
You can simplify the approach by outputting the value you need to retrieve in the metadata of the div element. Then you can retrieve that value from the element reference in the event that's raised, something like this:
jQuery($ => {
$('.search-result-name').on('click', e => {
const $element = $(e.target);
var lawyerId = $element.data('lawyerid');
console.log(`/profile?lawyerId=${lawyerId}`);
// window.location.assign(`/profile?lawyerId=${lawyerId}`); // commented for this demo only, uncomment in production
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<div class="search-result-info">
<div class="search-result-name" data-lawyerid="{{ item.id }}">{{ item.firstName }} {{ item.lastName }}</div>
</div>
That being said, having a div be clickable and having it redirect to another page is not good practice. I would suggest you convert the HTML to use an <a /> element, and output the data directly in to the href attribute. That way you don't need any JS at all:
<div class="search-result-info">
{{ item.firstName }} {{ item.lastName }}
</div>

Pre-populate current value of WTForms field in order to edit it

I have a form inside a modal that I use to edit a review on an item (a perfume). A perfume can have multiple reviews, and the reviews live in an array of nested documents, each one with its own _id.
I'm editing each particular review (in case an user wants to edit their review on the perfume once it's been submitted) by submitting the EditReviewForm to this edit_review route:
#reviews.route("/review", methods=["GET", "POST"])
#login_required
def edit_review():
form = EditReviewForm()
review_id = request.form.get("review_id")
perfume_id = request.form.get("perfume_id")
if form.validate_on_submit():
mongo.db.perfumes.update(
{"_id": ObjectId(perfume_id), <I edit my review here> })
return redirect(url_for("perfumes.perfume", perfume_id=perfume_id))
return redirect(url_for("perfumes.perfume", perfume_id=perfume_id))
And this route redirects to my perfume route, which shows the perfume and all the reviews it contains.
This is the perfume route:
#perfumes.route("/perfume/<perfume_id>", methods=["GET"])
def perfume(perfume_id):
current_perfume = mongo.db.perfumes.find_one({"_id": ObjectId(perfume_id)})
add_review_form = AddReviewForm()
edit_review_form = EditReviewForm()
cur = mongo.db.perfumes.aggregate(etc)
edit_review_form.review.data = current_perfume['reviews'][0]['review_content']
return render_template(
"pages/perfume.html",
title="Perfumes",
cursor=cur,
perfume=current_perfume,
add_review_form=add_review_form,
edit_review_form=edit_review_form
)
My issue
To find a way to get the review _id in that process and have it in my perfume route, so I can pre-populate my EditReviewForm with the current value. Otherwise the form looks empty to the user editing their review.
By hardcoding an index (index [0] in this case):
edit_review_form.review.data = current_perfume['reviews'][0]['review_content']
I am indeed displaying current values, but of course the same value for all reviews, as the reviews are in a loop in the template, and I need to get the value each review_id has.
Is there a way to do this, before I give up with the idea of allowing users to edit their reviews? :D
Please do let me know if my question is clear or if there's more information needed.
Thanks so much in advance!!
UPDATE 2:
Trying to reduce further my current template situation to make it clearer:
The modal with the review is fired from perfume-reviews.html, from this button:
<div class="card-header">
<button type="button" class="btn edit-review" data-perfume_id="{{perfume['_id']}}" data-review_id="{{review['_id']}}" data-toggle="modal" data-target="#editReviewPerfumeModal" id="editFormButton">Edit</button>
</div>
And that opens the modal where my form with the review is (the field in question is a textarea currently displaying a WYSIWYG from CKEditor:
<div class="modal-body">
<form method=POST action="{{ url_for('reviews.edit_review') }}" id="form-edit-review">
<div class="form-group" id="reviewContent">
{{ edit_review_form.review(class="form-control ckeditor", placeholder="Review")}}
</div>
</form>
</div>
Currently this isn't working:
$(document).on("click", "#editFormButton", function (e) {
var reviewText = $(this)
.parents(div.card.container)
.siblings("div#reviewContent")
.children()
.text();
$("input#editReviewContent").val(reviewText);
});
and throws a ReferenceError: div is not defined.
Where am I failing here? (Perhaps in more than one place?)
UPDATE 3:
this is where the button opens the modal, and underneath it's where the review content displays:
<div class="card container">
<div class="row">
<div class="card-header col-9">
<h5>{{review['reviewer'] }} said on {{ review.date_reviewed.strftime('%d-%m-%Y') }}</h5>
</div>
<div class="card-header col-3">
<button type="button" class="btn btn-success btn-sm mt-2 edit-review float-right ml-2" data-perfume_id="{{perfume['_id']}}" data-review_id="{{review['_id']}}" data-toggle="modal" data-target="#editReviewPerfumeModal" id="editFormButton">Edit</button>
</div>
</div>
<div class="p-3 row">
<div class=" col-10" id="reviewContent">
<li>{{ review['review_content'] | safe }}</li>
</div>
</div>
</div>
You can do this with jQuery as when you open the form, the form will automatically show the review content in there. It will be done by manipulating the dom.
Also, add an id to your edit button, in this example, I have given it an id "editFormButton".
Similarly, add an id to the div in which review content lies so that it is easier to select, I have given it an id "reviewContent"
Similarly, add an id to edit_review_form.review like this edit_review_form.review(id='editReviewContent')
<script>
$(document).on("click", "#editFormButton", function (e) {
var reviewText = $(this)
.parents("div.row")
.siblings("div.p-3.row")
.children("div#reviewContent")
.children()
.text();
$("input#editReviewContent").val(reviewText);
});
</script>
Don't forget to include jQuery.
Also, you can do it with pure javascript. You can easily search the above equivalents on google. This article is a good start!

How to add on click to list of products in html and get the item clicked to firestore

I'm trying to make a simple shopping cart using Django and Firebase Admin SDK, I have queried the Firestore data in a list and displayed them in HTML, now I want to add a click listener to the list of items and get the item clicked into a Firestore collection using javascript.
Now, when I click on an item other than the first item nothing happens, that is, when I click on the first item displayed on the page everything works out fine, but the other does not.
python view.py to display the items
def home(request):
collection_ref = db.collection(u'products').get()
documents = list(doc.to_dict() for doc in collection_ref)
return render (request,'store/home.html',{'product':documents})
home.html
{% block content %}
{% for doc in product %}
<div class="jumbotron">
<div class="col-sm-4">
<div class="card" >
<div class="card-body">
<a href="#" class="img-prod"><img class="img-fluid" id="productUrl" src="{{doc.productImage}}" alt="{{doc.productName}}">
<h1 class="card-title" id="productName">{{doc.name}}</h1>
<p class="card-subtitle mb-2 text-muted" id="productPrice">{{doc.price}}</p>
<p class="card-text" id="productDescription">{{doc.description}}</p>
<button type="button" id="addtocart">Add to cart</button>
</div>
</div>
</div>
</div>
{% endfor %}
{% endblock content %}
js code
firebase.initializeApp(firebaseConfig);
var db = firebase.firestore();
var addToCart=document.getElementById('addtocart');
addToCart.addEventListener('click', (event) =>{
var image=document.getElementById('productUrl').src;
var productName = document.getElementById('productName').innerHTML;
var productPrice= document.getElementById('productPrice').innerHTML;
db.collection("orders").add({
pName: productName,
price: productPrice,
url:image
})
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
})
You should always try to keep ids unique throughout the whole document. findElementById only returns the first element that it finds, so you add a listener to the first list item only when you run
var addToCart=document.getElementById('addtocart');
addToCart.addEventListener('click', (event) =>{...
You should define an add_to_cart function in your javascript that takes parameters and then in your for loop in the html you can have something like
<button onclick="add_to_cart({{doc.name}},{{doc.price}})>Add to cart</button>"

jQuery script works just for one button on the product catalog

I have a product catalog and I want to save on localstorage the products selected by the user.
The jquery script only gets the first product on each page on each click... it simply ignores the rest of the products, and the console prints the same object.
Here is my HTML+TWIG code
{% for products in pagination %}
<div class="product col-sm-6 col-lg-6 col-md-6 hero-feature">
<div id="{{attribute(products ,'id')}}" class="product thumbnail">
<img id="prodImage" class="img-responsive img-rounded" src="{{attribute(products ,'image')}}" style="width:150px;height:150px" >
<div class="product caption">
<h4 id="prodPrice" class="product pull-right"><b>{{ attribute (products, 'price') }}Lei</b></h4>
<h4 id="prodName" style="height:100px;width:200px;">
<a id="prodLink"
href="{{ attribute (products, 'affiliatelink') }}"
target="_blank">{{attribute ( products, 'name') }}</br></a>
</h4>
</div>
<div class="add-to-cart" class="product" >
<button id="buttonProd" class="oneButton btn btn-danger " value="Save" type="button">Adauga</button>
</div>
</div>
</div>
{% endfor %}
Here is the jquery script
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
<script type='text/javascript' defer="defer">
$(document).ready(function() {
var prodId = $('.thumbnail').attr("id");
$("#buttonProd").on("click", 'button', function(event){
event.preventDefault();
var products =
{
prodID :$('.thumbnail').attr("id"),
prodName :$('#prodName').text(),
prodPrice :$('#prodPrice').text(),
prodImage :$('#prodImage').attr('src'),
prodLink :$('#prodLink').attr('href')
};
localStorage.setItem('products-' + prodId, JSON.stringify(products));
var retrievedObject = JSON.parse(localStorage.getItem('products-' + prodId));
console.log('retrievedObject: ', retrievedObject);
});
});
</script>
How can I make the script take each product proprieties on click. Thank you in advance.
In JQuery, the assumption is made that all ID's will be unique. Since you're repeating "#buttonProd", JQuery will only select the first one to bind the action to. If you want to bind to multiple elements, you'll either have to give each button a unique ID or use some other selector to attach your jQuery functionality.
From the documentation for the ID Selector:
Calling jQuery() (or $()) with an id selector as its argument will return a jQuery object containing a collection of either zero or one DOM element.
As the other answers have eluded to you should be using a different selector. I recommend simply adding a descriptive class to each element you wish to grab data from.
<div class="products">
<div class="product">
<div class="productName">First Product</div>
<div class="productPrice">5.00</div>
</div>
<div class="product">
<div class="productName">Second Product</div>
<div class="productPrice">4.00</div>
</div>
<button id="buttonProduct">Log Product Info</button>
</div>
If you notice in the above HTML each div that contains a product's name or a product's price shares the same class productName and productPrice respectively. In addition each container class for each product has the same class as well: product.
This will allow us to utilize the JQuery class selector $(".") to iterate over each product container. We do this using the .each() function. We use the .find() function to locate productName and productPrice in each iteration of the loop.
$(document).ready(function() {
$("#buttonProduct").click(function(){
var products = [];
// Notice the dot in $(".product") that is the class selector
// the .each iterates over every element that matches the preceding selector
$(".product").each(function(){
products.push({
// The .find() selects an element inside $(this)
// that matches the parameter (either .productName or .productPrice
productName : $(this) .find('.productName').html(),
productPrice : $(this).find('.productPrice').html()
});
});
console.log(products);
});
});
For a working example of this check out this jsfiddle. (I noticed you had console.log() in your code so that's where I output the result.)
You want to save the product in localStorage on clicking of corresponding button right?
for that instead of binding click event via jquery, put it in html and move the click code to a function saveProduct()
HTML:
<button id="buttonProd" class="oneButton btn btn-danger " value="Save" type="button" onclick="saveProduct({{attribute(products ,'id')}})">Adauga</button>
JS:
function saveProduct(event, prod_id){
event.preventDefault();
var products =
{
prodID :prod_id,
prodName :$('#'+prod_id+' #prodName').text(),
prodPrice :$('#'+prod_id+' #prodPrice').text(),
prodImage :$('#'+prod_id+' #prodImage').attr('src'),
prodLink :$('#'+prod_id+' #prodLink').attr('href')
};
localStorage.setItem('products-' + prod_id, JSON.stringify(products));
var retrievedObject = JSON.parse(localStorage.getItem('products-' + prod_id));
console.log('retrievedObject: ', retrievedObject);
});
}
You need to change #buttonProd from an id to a class. An id is only supposed to appear once on a page, so jQuery will only apply it to one. Change it to a class in your markup and your script and it should work fine. Same for prodName, prodPrice, prodImage, and prodLink. Anything that will be going inside the loop needs to be a class, and any id should be unique, like you have {{attribute(products ,'id')}}

Jquery toggle (visible/invisible) for only the item clicked on in a template-generated html list

Code
<ul>
{% for item in lis %}
<li>
<div id="single-toggle">|Toggle|</div>
<div class="visible-when-folded">
<div class="name">{{ item.name }}</div>
<div class="date">{{ item.date }}</div>
</div>
<div class="invisible-when-folded">
<div class="about">{{ item.about }}</div>
<div class="contact_info">{{ item.contact_info }}</div>
</div>
</li>
{% endfor %}
</ul>
Example output code
|Toggle|
Peter
24-04-1990
A friendly guy
0474657434
|Toggle|
Martha
22-02-1984
An amazing gal
0478695675
|Toggle|
William
12-11-1974
An oldie
0478995675
Desired behavior
I would like that whenever you click on |Toggle| the about(e.g. A friendly guy)
and contact_info(e.g. 0474657434) part dissapear/reappear.
Attempt at solution
$(function(){
$("#single-toggle").click(
function(){ $("div.invisible-when-folded").toggle(); } );
});
But unfortunately this toggles the fields for each item in the list as opposed to only the one I click on.
Edit
views.py
from django.shortcuts import render_to_response
from django.template import RequestContext
def toggle(request):
lis = [{'name':'Peter', 'date':'24-04-1990', 'about':'A friendly guy',
'contact_info':'0474657434' },
{'name':'Martha', 'date':'22-02-1984', 'about':'An amazing gal',
'contact_info':'0478695675' },
{'name':'William', 'date':'12-11-1974', 'about':'An oldie',
'contact_info':'0478995675' }]
return render_to_response('page.html', {'lis':lis},
context_instance=RequestContext(request))
You need to pass the current object as context in the selector to get the element related to event source object. You also need to use class instead of id or generate unique ids for div with id = single-toggle as html elements are supposed to have unique ids.
Live Demo
I have give the div with id a class="single-toggle"
Change
$("div.invisible-when-folded").toggle();
To
$("div.invisible-when-folded", this).toggle();
You code
$(function(){
$("#single-toggle").click(
function(){ $("div.invisible-when-folded", this).toggle(); } );
});
You need to focus the function on the div within the clicked div... The actual code you need to use is:
$(function(){
$("#single-toggle").click(
function(){ $("div.invisible-when-folded", this).toggle(); } );
});

Categories

Resources