How do you properly clone something in Javascript/JQuery? - javascript

I'm using .clone() to duplicate a form. I make small changes to the clone and then place it after the last form. As you can see from the screenshot it's mostly working; they look identical. The application (Django/Python) can process the clone as well once I press save.
The problem is the calendar widget does not open when clicked (on the clone form). It does open if I click on the widget button for a form that already exists on the page when it first loads (not a clone). But on my clones the date picker does not open.
What it should look like after I click on it (instead nothing happens):
The cloned html seems to look identical in all the right ways.
Existing form:
Clone:
EDIT Event Listeners:
Looking at the Event Listeners for the tag the on click event seems to me missing.
Existing Form:
Clone:
Is something missing from the cloned html? Or is there something behind the scenes that is not working? I just don't understand what is broken here.
JS/JQuery:
function cloneMore(selector, prefix,form_class) {
var newElement = $(selector).clone(true);
var total = $('#id_' + prefix + '-TOTAL_FORMS').val();
newElement.find(':input:not([type=button]):not([type=submit]):not([type=reset])').each(function() {
var name = $(this).attr('name').replace('-' + (total-1) + '-', '-' + total + '-');
var id = 'id_' + name;
$(this).attr({'name': name, 'id': id}).val('').removeAttr('checked');
});
newElement.find('label').each(function() {
var forValue = $(this).attr('for');
if (forValue) {
forValue = forValue.replace('-' + (total-1) + '-', '-' + total + '-');
$(this).attr({'for': forValue});
}
});
total++;
$('#id_' + prefix + '-TOTAL_FORMS').val(total);
$(selector).after(newElement);
return false;
}
$(document).on('click', '.add-form-row', function(e){
e.preventDefault();
cloneMore('.form-row-payment:last', 'payment_invoice','form-row-payment');
return false;
});
HTML:
{{ payments_formset.management_form }}
{{ payments_formset.media }}
<h3>Payments</h3>
{% for formpay in payments_formset %}
<div class="form-row form-row-payment row container" name="payment_form" style="padding:0px;" id="payment_formset">
{{ formpay.non_form_errors }}
{{ formpay.non_field_errors }}
{% for hidden in formpay.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in formpay %}
{% if field.name != 'index' and field.name != 'invoice'%}
<div class="col-sm">
{{ field.errors }}
{{ field|as_crispy_field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endif %}
{% endfor %}
</div>
{% endfor %}
<div class="input-group-append">
<button class="btn btn-success add-form-row">+</button>
</div>

You are making a copy of the HTML objects, but these do not have the same event handlers as the original object. To remedy this, I would suggest making a function that will reset the event handlers for the cloned elements.
This could be done something like this:
function resetCalendarClickHandler(){
$(".input-group-addon").unbind("click") //remove current event handlers to prevent duplication when adding new one
$(".input-group-addon").click(<yourEventHandler>);
}
Once you have this, you can add a call to this function at the end of your cloneMore() function and this will re-assign the event handlers and include the cloned element as it will be part of your HTML at this point.

Related

Using Javascript to change text Content of a button

Hey guys I'm creating a social media messaging app and right now I'm on the basics of creating a button to follow people.
When you click the button, it should change the button from saying "Follow" to "UnFollow". Then the counter for Followers should go up by 1.
So far this is my code and it's not doing anything. I'm not getting any errors but it's also not doing anything at all.
Can someone figure out what I'm doing wrong? Thanks
network.js:
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('followButton').addEventListener('click', () => follow_user());
});
function follow_user() {
const element = document.getElementById('followButton');
element.value = "Un-Follow";
const element2 = document.getElementById('followers');
element2.textContent += 1;
}
profile.html :
{% extends "network/layout.html" %}
{% block body %}
<h2>{{user.username}}'s Profile</h2>
<p id="followers">Followers: {{user.followers}}</p>
<p>Follows: {{user.follows}}</p>
{% for newquery in newqueries reversed %}
<div class="newpost"><p>{{ newquery.body }}</p><p>By: {{ newquery.username }} on: {{ newquery.timestamp }} Likes: {{ newquery.likes }}</p></div>
{% endfor %}
<input type="button" value="Follow" id="followButton">
{% endblock %}
Your first problem is your follower count does not have an ID. You are trying to target it with #followers, but it only has a class .followers.
Your second problem is button inputs don't display textContent. They display their value.
const element = document.getElementById('followButton');
element.value = "Un-Follow";
Try something like this: const element = document.getElementById("followButton").value="Un-Button Follow" i think the value should be Button in between the un and follow i.e un-follow, Note(change it to suit your code)
Check this article here: https://www.permadi.com/tutorial/jsInnerHTMLDOM/index.html

Can't process ajax call on my page because Django request.is_ajax returns True when page is intially loaded

I have a page that makes ajax calls, and my view checks if the call is ajax with .is_ajax function. However, when the page is initially loaded (for example if I stop running the server and then restart it) the is_ajax function seems to return True, which causes a MultiValueDictKeyError at /alltext, because the request does not contain the key "alltext", which contains the data from my other ajax call.
The page is a e-commerce product page, and the product has different variants (i.e. size and color), and when the user chooses variants from a dropdown menu (i.e. Large, Blue), it makes an ajax call to the backend to retrieve the price of this specific variant from the database.
Here is my code:
views.py
def product_info(request):
if request.method == "GET" and not request.is_ajax: # this is supposed to be loaded on page load
return render(request, "product_info.html")
elif request.is_ajax and request.method == "GET":
print(request.is_ajax)
'''When a user chooses a product variant on the page, this makes an ajax call
to retrieve the price of this combination'''
print("request was ajax")
combinations = request.GET["alltext"]
combinations_list = combinations.split(";")
product = Product.objects.all().latest("id")
var_names = Variation.objects.filter(product=product)
corresponding_values = []
for i in range(len(combinations_list)):
# finding this variant in database
var_name = var_names[i]
var_values = VariationValue.objects.filter(variation_name=var_name)
for val_obj in var_values:
val = val_obj.value
if val == combinations_list[i]:
corresponding_values.append(val_obj)
found_price = None
for i in range(len(corresponding_values)):
val = corresponding_values[i]
if i == 0:
combo_query = VariationCombination.objects.filter(variation_value=val)
else:
combo_query = combo_query.filter(variation_value=val)
price = combo_query[0].price
return HttpResponse("You chose: " + combinations + "price: " + price)
And here is the relevant part of the product_info page - the forms where the user chooses the variants and the script that sends the variants to the backend:
product_info.html
<form class="variations-form">
{% for name in variation_names %}
<div class="form-group">
<label class="btn-label">{{ name.variation_name }}:</label>
<!--<button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown">
<span class="selection"></span><span class="caret"></span>
</button>-->
<select name="variation{{ forloop.counter }}" class="form-control variations" id="variation{{ forloop.counter }}">
<option value="" selected disabled>{{ name.variation_name }}</option>
{% for key, value_list in variation_values.items %}
{% if key == name.variation_name %}
{% for value in value_list %}
<option value="{{ value }}">{{ value }}</option>
{% endfor %}
{% endif %}
{% endfor %}
</select>
</div>
<br>
{% endfor %}
</form>
Script tag with ajax call
<script>
$(document).ready(function () {
$('.form-control').change(function(){
if ($('#variation1').val()) {
var valueFrom = $('.variations option:selected').map(function () {
return $(this).val();
}).get();
var alltext = valueFrom.join(";")
$('#chosen-options').html(alltext).show('fast');
$.ajax(
{
type:"GET",
data:{
'action':'options_chosen',
alltext: alltext
},
success: function( data )
{
$('#chosen-options').html(data).show('fast');
}
});
};
});
});
</script>
Still not entirely sure what was wrong but I fixed it! At first, the page was setup so that it loads the template "homepage.html" at first, and then when you click on a product it opens in the same page, by loading the template "product_info.html". So I just changed it so that the product_info opens up on a new page when you click a product (and added the new page to urls.py), and now it works!

Dynamically generate hyperlink with keypress (without submit) in Flask App

https://jsfiddle.net/mjcyezh0/
I'm creating a Flask webapp for work and on the main page, I ask my users if they're an interviewer at the company. If they select YES, I prompt them for their ID (i.e. 290).
In another space on the page, I've used jinja2 to programmatically loop through a dictionary of questions and links to generate a series of questions, hyperlinks and inputs for my user; this will allow them to answer questions and have helpful links that are customized for the current time/date.
One of the hyperlinks sends the user to a custom search in our interviewer platform, but the only way to direct them to the right place is to have that ID within the URL, (i.e: website.com/###/interviewers/.....)
Questions re: Javascript
Is there anyway that using .keypress and (no onclick/button submission), I can dynamically populate the content WITHIN the href of that particular link? (post-completion is fine)
If yes, how can I fit this into my jinja2 {% for %} loop to address the ONE specific entry? (row 12, when `x['tech_key'] == 'int') [That is, do I want to add a particular ID/CLASS to it in order to run the js code?]
Code attached for my for-loop:
1 <div class="col-md-8">
2
3 {% for k,v in inps['sheet'].items() %}
4 {% for x in v %}
5 {% if x['question'] is not number %}
6
7 <div class="{{x['tech_key']}}">
8 <hr class="thicker">
9 <b>{{x['question']}}</b>
10 <button class="add_form_field" data-key="{{x['tech_key']}}"><b>+</b></button><br>
11 {% for i in ['1', '2', '3'] if x['link'+i] is not number%}
12** {{x['text'+i]}}{{ " | " if not loop.last }}
13 {% endfor %}<br>
14
15 {% if x['subtext'] is not number%}
16 <i><p style="font-size: 12px;overflow:auto">{{x['subtext']}}</p></i>
17 {% endif %}
18 <div><input type="text" name="{{x['tech_key']}}1" size="80px"></div>
19 </div>
20 {% endif %}
21 {% endfor %}
22 {% endfor %}
23 </div>
You need the build the link URL after retrieving the interviewer id:
First give your elements an id:
<input type="text" id="form_interviewer_id" name="interviewer_id" value="" >
<a id="linkAnchor" href="https://example.com/interviewer/0?start_date=2020-07-01">palp</a>
Then you can access them via JavaScript:
window.onload = function() {
var textField = document.getElementById("form_interviewer_id");
textField.onkeyup = function(evt) {
evt = evt || window.event;
if (evt.keyCode == 13 || this.value.length == 2) { // Updated
var linkAnchor = document.getElementById("linkAnchor");
linkAnchor.href = getLink(textField.value);
}
};
};
function getLink(id) {
return "https://example.com/" + id + "/0?start_date=2020-07-01";
}
Updated Fiddle: https://jsfiddle.net/63onu9dm/ [Updated]

HTML Button is non-responsive, console doesn't register event

Working on a simple to-do app, but having a problem with the delete button. When I click, the browser console doesn't even register an event. Super confused on what to do here. I've gone through all my code in index.html as well as the app.py code, I can't figure out why it's not even registering in the browser that something happened, even if it doesn't lead to what I want it to do. Basically just not sure how to debug something that's not even registering as existing I guess?
I'm assuming it's a problem in the HTML but just in case, I'm including the relevant python code as well.
Thanks!
index.html
<ul id="todos">
{% for d in data %}
<li><input class="check-completed" data-id="{{ d.id }}"
type="checkbox" {% if d.completed %} checked {% endif %}> {{ d.description }}
<button class="li button deletebutton" data-id="{{ d.id }}">&cross;</button> <!-- &cross; is HTML for an X-->
</li>
{% endfor %}
</ul>
script in index.html:
const deletebtns = document.querySelectorAll('.deletebutton');
for (let i = 0; i < deletebtns.length; i++) {
const deletebutton = deletebtns[i];
deletebutton.onclick = function(e) {
const todoId = e.target.dataset['id'];
fetch('/todos/' + todoId, {
method: 'DELETE',
})
}
}
}
app.py:
#app.route('/todos/<todo_id>', methods=['DELETE'])
def delete_todo(todo_id):
try:
Todo.query.filter_by(id=todo_id).delete()
db.session.delete(todo)
db.session.commit()
except:
db.session.rollback()
finally:
db.session.close()
return redirect(url_for('index'))

Use a javascript array to display specific HTML divs

Using django I have a HTML page with a table containing checkboxes:
{% for o in obj %}
<input class='germ_ids' name='{{o.id}}'>
{% endfor %}
<script>
var elements = document.getElementsByClassName("germ_ids");
var ids = [];
for(var i=0; i<elements.length; i++) {
ids.push(elements[i].name);
}
</script>
This has given me an array of specific 'germ ids'. These relate to other django objects I have:
the same .html page:
{% for k, v in add_state_dict.items %}
<div class='{{k.id}}_statement'>
<p>{{v}}</p>
</div>
{% endfor %}
<div class='all_germ'>
<p>{{additional_statement}}</p>
</div>
What I want to achieve is a statement for each germ_id when their checkbox is checked. However when more than one boxes are checked, it makes a concat statement called additional_statement in the 'all_germ' class.
I have hard coded it in in a JSFiddle http://jsfiddle.net/sDsCM/1037/
However I want to use the elements in the array to do this to account for different array values.
You should not be using class names with IDs but instead use class names that categorize your checkboxes. As example I'll be using .checkbox-plus-info. Also, use a data attribute to find the associated statement (unless you can render them inside one div):
{% for o in obj %}
<input class='germ_ids checkbox-plus-info' name='{{o.id}}'
data-info-tag="info-{{o.id}}">
{% endfor %}
{% for k, v in add_state_dict.items %}
<div id='{{k.id}}_statement' class="info info-{{k.obj_id}}">
<p>{{v}}</p>
</div>
{% endfor %}
In the above, k of course needs to contain a reference to the object (its PK/ID). You should have that information in your view and add it to the context. You haven't posted your view/context code but the business logic should mostly be ready while you create the template context, so prepare as much as you can there.
In your JS:
$(".checkbox-plus-info").click(function() {
$(".info").hide(); // hide all
stmtElId = $(this).target.data("info-tag");
$("#" + stmtElId).show();
// check whether something has to be shown when several checkboxes are checked
// e.g. count whether all checkboxes are checked, or other logic
});

Categories