javascript - replicate a div on button click - javascript

I have a html with various form elements:
<div class="card border-bg-light mb-3" id="card_steps">
<div class="card-header bg-transparent" id="card_header">
<span class="pull-right clickable close-icon" data-effect="fadeOut"><i class="fa fa-times"></i></span>
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.step_description.errors }}
<label for="{{ form.step_description.id_for_label }}">Step:</label>
{{ form.step_description }}
</div>
</div>
<div class="card-body" id="card_body">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="fieldWrapper">
{{ form.can_edit_email.errors }}
<label for="{{ form.can_edit_email.id_for_label }}">Editable:</label>
{{ form.can_edit_email }}
</div>
<div class="fieldWrapper">
{{ form.step_notification_period.errors }}
<label for="{{ form.step_notification_period.id_for_label }}">Notify user before launch
date:</label>
{{ form.step_notification_period }}
</div>
<div class="fieldWrapper">
{{ form.step_attachment.errors }}
<label for="{{ form.step_attachment.id_for_label }}">Upload:</label>
{{ form.step_attachment }}
</div>
</form>
</div>
</div>
In my next card div i have a link Add Steps which upon clicking will create a div which is exactly the same as the div card_steps above and it will be right below the card_steps div, so how do i go about doing this in javascript?
<div class="card">
<div class="card-body">
+ Add Step
</div>
</div>

Put an onclick on your button in your html:
onclick="createClone()"
then set a method to clone a div in your js:
function createClone() {
var div = document.getElementById('div_id'),
clone = div.cloneNode(true); // true means clone all childNodes and all event handlers
clone.id = "some_id";
document.body.appendChild(clone);
}
found at this answer.

You can copy a div's INNER contents quite easily using:
document.getElementById("add_step").innerHTML
Once you have that, you essentially just cloned the INNER contents of that div. Everything. If you want the shell of the div, you will need to recreate it manually. For example, you could do this:
var x = document.getElementById("add_step"); // the clone's parent element.
var y = document.createElement("div"); // the clone's inner content
y.innerHTML = document.getElementById("card_steps").innerHTML;
x.appendChild(y);
There, you have essentially just cloned your div.
EDIT: Edited to better suit your question with correct element ID names.

Related

Add remove with numbering indicated dynamically

So currently I can easily add and remove multiple textarea
But what I'm trying to do is to put numbering per specific textarea
Here's my textarea
as you notice my default text area is Step 1
But I wanted to do is when I clicked add it will show another textarea that says
Step 2
<div class="optionBox">
<div class="block">
<div class="form-group">
<label for="exampleFormControlTextarea1">Step 1</label>
<textarea class="form-control rounded-0" id="exampleFormControlTextarea1" rows="10"></textarea>
</div>
<span class="remove">Remove</span>
</div>
<div class="block"> <span class="add">Add Option</span>
</div>
</div>
My Javascript
$('.add').click(function () {
$('.block:last').before(' <div class="block"><div class="form-group"><label for="exampleFormControlTextarea1">Step</label><textarea class="form-control rounded-0" id="exampleFormControlTextarea1" rows="10"></textarea></div><span class="remove">Remove</span></div>');
});
Here's the current output
To make this work you can add a span element with no content to the label within each .block. Then you can create a function which updates the number within that span based on the index of the element every time you add or remove a .block.
I would also strongly suggest that you clone elements instead of adding lots of HTML in to your JS logic, as this violates the Separation of Concerns principle due to tying the JS too closely with the HTML. In your case this can be done by simply adding one extra class on to the .block which holds the textarea elements. Try this:
$('.add').click(function() {
let $lastBlock = $('.block.step:last');
let $clone = $lastBlock.clone().insertAfter($lastBlock);
$clone.find('textarea').val('');
updateStepCounts();
});
$('.optionBox').on('click', '.remove', function() {
if ($('.block.step').length > 1) {
$(this).closest('.block').remove();
updateStepCounts();
}
});
let updateStepCounts = () => $('.block label span').text(i => i + 1);
updateStepCounts();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="optionBox">
<div class="block step">
<div class="form-group">
<label for="exampleFormControlTextarea1">Step <span></span></label>
<textarea class="form-control rounded-0" id="exampleFormControlTextarea1" rows="10"></textarea>
</div>
<span class="remove">Remove</span>
</div>
<div class="block">
<span class="add">Add Option</span>
</div>
</div>
I removed the div.block surrounding the "add option" span and used .append on the newly added div.block-container element instead of .before to add the next textarea to the bottom of the options container. i think this reads a little better symantically.
Also using string interpolation i am able to insert the total number of div.block + 1 to track the number of textareas visible on the page.
Hope this helps.
$('.add').click(function () {
const numberOfBlocks = $(".block").length;
$(".block-container")
.append(`
<div class="block">
<div class="form-group">
<label for="exampleFormControlTextarea1">Step ${numberOfBlocks + 1}</label>
<textarea class="form-control rounded-0" id="exampleFormControlTextarea1" rows="10"></textarea>
</div>
<span class="remove">Remove</span>
</div>`
);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="optionBox">
<div class="block-container">
</div>
<span class="add">Add Option</span>
</div>

JQuery hide/show div based on select options

JQuery noob here, I'm trying to make appear/disappear divs depending on multiple form select options.
My problem is that the div hide successfully, but I can't manage to make them appear back again when an option is selected.
Here is my JQuery code
<script type="text/javascript">
$(document).ready(function () {
$('#areaFields').hide();
$('#areaItems').hide();
$('#areaPartners').hide();
$('#hidden3').hide();
$('#items').hide();
$('#partners').hide();
$('#fields').hide();
$('#hidden2').hide();
$('#block').hide();
$('#ORDER').hide();
$('#type').change(function () {
$('#ORDER').hide();
$('#' + $(this).val()).show();
});
});
</script>
and here is my HTML
<div class="container">
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Customizing</legend>
<div class="container">
<div class="form-group default1">
<div class="row">
<div class="col">
<div id="taille" class="form-group">
{{ form.tailleDocX.label }}
{{ form.tailleDocX }}
{{ form.tailleDocY.label }}
{{ form.tailleDocY }}
</div>
</div>
</div>
<div class="row">
<div class="col">
<div id="type" class="form-group">
{{ form.type.label }}
{{ form.type }}
</div>
</div>
<div class="col">
<div id="customer" class="form-group">
{{ form.customer.label }}
{{ form.customer }}
</div>
</div>
</div>
</div>
<div id="ORDER" class="form-group">
<hr>
<div class="row">
<div class="col">
<div id="block" class="form-group">
{{ form.block.label }}
{{ form.block }}
</div>
</div>
</div>
<div id="hidden2" class="form-group">
<hr>
<div class="row">
<div class="col">
<div id="items" class="form-group">
{{ form.itemsFields.label }}
{{ form.itemsFields }}
</div>
<div id="partners" class="form-group">
{{ form.partnersFields.label }}
{{ form.partnersFields }}
</div>
<div id="fields" class="form-group">
{{ form.fields.label }}
{{ form.fields }}
</div>
</div>
</div>
</div>
<div id="hidden3" class="form-group">
<hr>
<div class="row">
<div class="col">
<div id="areaFields" class="form-group">
{{ form.areaFields }}
</div>
<div id="areaItems" class="form-group">
{{ form.areaItems }}
</div>
<div id="areaPartners" class="form-group">
{{ form.areaPartners }}
</div>
</div>
</div>
</div>
<div class="form-group default2">
<hr>
<div class="row">
<div class="col">
{{ form.submit }}
</div>
</div>
</div>
</div>
</div>
</fieldset>
</form>
</div>
To create the form I'm using WTForm from FLASK, if needed here is the code :
class AreaFields(FlaskForm):
area1 = IntegerField("X0")
area2 = IntegerField("X1")
area3 = IntegerField("Y0")
area4 = IntegerField("Y1")
textArea = StringField("Texte")
class AreaItems(FlaskForm):
name = StringField("Name")
class AreaPartners(FlaskForm):
area1 = IntegerField("X0")
area2 = IntegerField("X1")
area3 = IntegerField("Y0")
area4 = IntegerField("Y1")
rolePartners = SelectField("Role", id="role")
class AjouterForm(FlaskForm):
type = SelectField("Type",
choices=[("INVOICE", "invoice"), ("ORDER", "order"), ("SERVICE_REQUEST", "service_request")],
id="type")
block = SelectField("Block", choices=[("ITEMS", "items"), ("PARTNERS", "partners"), ("fields", "fields")],
id="block")
itemsFields = SelectField("Champs", id="items")
partnersFields = SelectField("Champs", id="partnersFields")
fields = SelectField("Champs", id="fields")
tailleDocX = IntegerField("Taille X", validators=[DataRequired()])
tailleDocY = IntegerField("Taille Y", validators=[DataRequired()])
customer = SelectField("Customer", choices=[("NotFinalData", "NotFinalData")])
areaFields = FormField(AreaFields)
areaItems = FormField(AreaItems)
areaPartners = FormField(AreaPartners)
submit = SubmitField('Voir le résumer')
For the moment what I want is : to hide all the div like I did, then show the div #ORDER if order is selected in the form, or to hide the div again if another option is selected, then I'll do the same for the others select and div once I can manage to make it work.
Thanks for reading !
Edit 1 : My first select that manage the first hide/show is form.type, it contains 3 options : INVOICE, ORDER, SERVICE_REQUEST
When I select INVOICE or SERVICE_REQUEST, everything must stay hide because thoose option aren't ready for the moment, but when I select ORDER, I wish to make appear the divs #ORDER and #block. #ORDER contains all the other divs specific to the ORDER option, #block contain another select that will make appear/disappear another div depending on the option selected.
Here is a picture to show the actual form without being hidden
The first select is Type, it must make appear Block if order is selected, and block make appear a Champs depending on the option selected.
Edit 2 : BIG NEWS, I just found out what was my problem, it's so dumb of me for not seeing this at first, I used the same ID for the div and for the WTForm select... so basicly I was checking for changement for the div and not for the select... I just feel so dumb right now, thank you for trying to help me !
You just have to hide only the element your previously showed. your code should look like this.
<script type="text/javascript">
$(document).ready(function () {
var current_element = '';
$('#areaFields').hide();
$('#areaItems').hide();
$('#areaPartners').hide();
$('#hidden3').hide();
$('#items').hide();
$('#partners').hide();
$('#fields').hide();
$('#hidden2').hide();
$('#block').hide();
$('#ORDER').hide();
$('#type').change(function () {
var id = $(this).val()
$('#' + id).show();
// hide previously showed element
if(current_element != '') $('#' + current_element).hide();
// store id of previously selected element
current_element = id
});
});
</script>

How to stop all items from being opened when editing item in ngFor loop

I have an array of objects and you can edit the name of each one but then I click to edit one all of the names of the items open, I am wondering how do to fix this.
<div *ngFor="let stop of fave; let i = index" attr.data="{{stop.Type}}">
<div class="card m-1">
<div class="card-body">
<div class="card-text">
<div class="row">
<label class="name" *ngIf="!toggleName" (click)="toggleName = true">{{stop.Name}}</label>
<div class="md-form" *ngIf="toggleName">
<input (keydown.enter)="updateStopName(i, stop.id); toggleName = false" placeholder="Chnage Stop Name" [(ngModel)]="stopName" required mdbInput type="text"
id="form1" class="form-control">
</div>
</div>
<div class="custom">
<img *ngIf="stop.Type === 'Train'" class="train-icon" style="width: 40px; height:40px"
src="assets/img/icon_trian.png" />
<img *ngIf="stop.Type === 'bus'" style="width: 40px; height:40px" src="assets/img/icon_bus.png" />
<img *ngIf="stop.Type === 'Luas'" style="width: 40px; height:40px"
src="assets/img/icon_tram.png" />
</div>
<label class="col-4 custom-label">Stop</label>
<label class="col-5 custom-service-label">Service</label>
<div class="row">
<span class="col-5 stop"> {{stop.StopNo}}</span>
<span style="padding-left:31%;" class="col-6 stop"> {{stop.Type | titlecase}}</span>
</div>
<hr />
<div class="row">
<div class="panel col-7" (click)="getRealtimeInfo({stop: stop.StopNo, type: stop.Type})">
<img class="panel-realtime" src="assets/img/icon_view.png" />
</div>
<div class="panel col-5" (click)="deleteFav(stop.id, i)">
<img class="panel-remove" src="assets/img/icon_remove.png" />
</div>
</div>
</div>
</div>
</div>
</div>
I know its something to do with the index but I am not sure how to write the code to only open the one I clicked on
As you can see at the moment all of them open any help is very much appreciated
If you want to open one at a time, you can use the index and of the item and a boolean. When clicked, set the index value to toggl if it's not already assigned, else assign it null (so that we can close the opened div on same click), and then show the content you want, when toggl === i. Something like:
<div *ngFor="let stop of fave; let i = index">
<label (click)="toggl === i ? toggl = null : toggl = i">Stuff!</label>
<div *ngIf="toggl === i">
<!-- ... -->
</div>
</div>
DEMO: StackBlitz
In your component declare one array
hideme=[];
In your html
<div *ngFor="let stop of fave; let i = index" attr.data="{{stop.Type}}">
<a (click)="hideme[i] = !hideme[i]">show/hide</a>
<div [hidden]="hideme[i]">The content will show/hide</div>
</div>
You have a unique id value inside your array, then you can do it like this:
<div *ngFor="let row of myDataList">
<div [attr.id]="row.myId">{{ row.myValue }}</div>
</div>
Assign an id to your input fields and they will work fine. Right now all of them have same id.
Use this code below as an example:
In your component, create a mapping like so:
itemStates: { [uniqueId: string]: boolean } = {};
Within your on click function:
itemClicked(uniqueId: string) {
let opened: boolean = this.itemStates[uniqueId];
if (opened !== undefined) {
opened = !opened; // Invert the result
} else {
opened = true;
}
}
In your HTML:
<div *ngFor="let item of items">
<h1 (click)="itemClicked(item.uniqueId)">{{ item.name }}</h1>
<div *ngIf="itemStates[item.uniqueId] == true">
<p>This item is open!</p>
</div>
</div>
Essentially, each item in your array should have a unique identifier. The itemStates object acts as a dictionary, with each unique ID having an associated true/false value indicating whether or not the item is open.
Edit: The accepted answer to this question is very simple and works great but this example may suit those who need to have the ability to have more than one item open at once.

Find a <div> which contains a <p> with a given text inside

I have an application which presents several <div> blocks, each one with a given id. Each block has an inner <p name="barcode"></p> tag.
I would like to know how to, inside a Javascript function, search for the <p name="barcode"></p> which contains a given barcode and return the id of the container <div>.
It could use jQuery or Javascript, whatever is easier. I could also assign a given attribute or class to the container <div>if that makes easier to find it.
Note: the code uses {{ }} for variables, as pages are generated in a Jinja2, but the question relates to regular jQuery.
<div id="{{ item.id }}" position="{{ item_number }}" href="#" state="{{ item.state }}" class="popup-box bs-callout bs-callout-{{ item.state }}">
<div class="row">
<div class="col-xs-10">
<div class="row">
...
<div class="col-xs-12">
<p name="barcode" style="color: grey; padding: 0px; margin: 0px;">{{ item.barcode }}</p>
</div>
...
</div>
...
</div>
....
</div>
</div>
...
Using jQuery, you can search for a div of class .barcode that contains specific text like this:
$(".barcode:contains(some_specific_text)")
You can then get the parent using the .parent() method-- if you know that that it is unique, you don't have to filter it down, otherwise you might want to use .eq(). Then you can get the id with .attr('id').
Put it all together:
$(".barcode:contains(some_specific_text)").parent().attr('id')
You can access by giving a class name to your div tag and then access via the following jquery. Try this and see
<div id="{{ item.id }}" position="{{ item_number }}" href="#" state="{{ item.state }}" class="popup-box bs-callout bs-callout-{{ item.state }}">
<div class="row">
<div class="col-xs-10">
<div class="row">
...
<div class="col-xs-12 item">
<p name="barcode" style="color: grey; padding: 0px; margin: 0px;">{{ item.barcode }}</p>
</div>
...
</div>
...
</div>
....
</div>
</div>
...
JQUERY
$(".barcode:contains('Test')").parent().attr('item.id')
Please try this
$('[name="barcode"]').closest(".popup-box").attr('id');

In meteor, how to create dynamic id with same class name

I have a card displaying dynamically when I submit the form.
Here is code:
<template name="workflow">
<div id="newActionCard">
{{#each newaction}}
<div class="workflowcard">
<div class="module-card-small">
<div class="res-border"></div>
<div class="card-img">{{team}}</div>
<div class="res-content">
<div class=" newaction-name">{{action_title}}</div><hr>
<div class="newaction-des">{{description}}</div>
<!-- <div class=" due-on">Due on:{{d_date}}</div><hr>-->
</div>
<div class="due">
Due on:
<div>
<div class="day-stamp">{{weekday d_date}}</div>
<div class="date-stamp">{{date d_date}}</div>
<div class="month-stamp">{{month d_date}}</div>
</div>
</div>
{{> actioncardsubcontent}}
</div>
<div class="btn-box">
<button type="button" class="cancelsub">New Action</button>
<button type="submit" class="createbtnsub">Show Options</button>
</div>
</div>
{{/each}}
</div>
</div>
</template>
<template name="actioncardsubcontent">
<div class="subcontent">
<div class="modulepath"><div>{{module_list}}</div></div>
<div class="linkto"><div>Linked To: <div class="linkto-color">{{link}}</div></div></div>
<div class="description"><div>Notes:<br>{{description}}</div></div>
</div>
</template>
when I submit the form the card gets generated with some hidden part by the below JS.
Template.actioncardsubcontent.rendered = function(){
this.$(".subcontent").hide();
};
When I click on "Show Options" button the hidden part will display. and below is the code for that
Template.workflow.events({
"click .createbtnsub":function(){
$('.subcontent).show();
}
});
My question is, when I click on show options button at the output the hidden part is displaying for each card at a time not to the particular card. The reason is I have given the same class name for each and I want to create a dynamic id that allos particular card hide content on click. How to write it. I am stuck there. Can anyone help?
I have tried with below code but it doesn't work.
<div class="action" id={{_id}}> {{> actioncardsubcontent}} </div>
and in JS i replaced $('.subcontent').show(); with var id="#"+this._id; $(id).show(); but it doesn't work. Let me know how to I write for dynamic Id generation for each click.
The idea of your id={{_id}} solution is correct, but you have added the id attribute to a different element than what you hide. Try this:
<template name="actioncardsubcontent">
<div class="subcontent" id={{_id}}>
...
</div>
</template>
Your event handler in the upper template should still find the element if you do
$('#' + this._id).show();
If this does not help, it may be because your data does not have an _id attribute. In that case you could use some other unique identifier or add one with Random.id().
You can try this as this has worked for me.
function(){
$('.className').each(function(i) {
$(this).attr('id', 'idName'+i);
});

Categories

Resources