Hi im trying to create a form builder im approaching this by making creating elements putting them is a list group then making them draggable. This works so far however when i drag in item and place it the it disappears from the elements section this makes sense but i would like it to find the id of the item that has been selected and then copy the element but with a different id.
This is my code so far
JavaScript
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
console.log("Component Selected")
var elem = document.querySelector(ev.target.id);
var clone = elem.cloneNode(true);
clone.id = ('drag9');
elem.after(clone);
}
<div class="row">
<div id="div1" ondrop="drop(event)" ondragover="allowDrop(event)">
<ul class="list-group">
<li draggable="true" ondragstart="drag(event)" id="drag1" class="list-group-item">
<label for="exampleFormControlInput1" class="form-label">Input</label>
<button type="button" class="fa-solid fa-pen-to-square fa-lg"></button>
<input type="email" class="form-control" id="ControlInput1" placeholder="">
</li>
</ul>
</div>
</div>
This type of dynamic page creation functionality has to be done without id attributes, they are error prone and a nightmare to maintain.
Drag'n'Drop functionality consists of multiple events. You shouldn't do everything on dragstart, dropping has its own event - drop - during which you're supposed to append the element to the target element. And, never use inline listeners for elements. In this case it's usefull to delegate dragstart event to the ul container.
When you drag an element, it should be dropped only on an element, which is an allowed parent to the element to drop (the only allowed parents of li element are menu, ul and ol, it shouldn't be dropped on a div).
As Drag'n'Drop API doesn't provide a property where you can store a live reference to the actually dragged element, it's best to store it in a variable when dragstart event fires. This tackles the need of id attributes and all unnecessary DOM traversing.
All this in mind, I've created a fully idless example code of how to implement a simple drag'n'drop, you can develope it further for your needs.
const dragSrc = document.body.querySelector('.dragSrc'),
dropPad = document.body.querySelector('.dropTarget');
let draggedElement = null;
dragSrc.addEventListener('dragstart', e => {
draggedElement = e.target;
});
dropPad.addEventListener('dragover', e => {
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
});
dropPad.addEventListener('drop', e => {
e.preventDefault();
const clone = draggedElement.cloneNode(true);
e.target.appendChild(clone);
});
.dropTarget {
position: relative;
width: 400px;
height: 100px;
border: 1px solid #000;
}
<div class="row">
<ul class="list-group dragSrc">
<li draggable="true" class="list-group-item">
<label class="form-label">Email:
<input type="email" class="form-control" placeholder="">
</label>
<button type="button" class="fa-solid fa-pen-to-square fa-lg">Button</button>
</li>
</ul>
</div>
<ul class="list-group dropTarget"></ul>
Related
I have some spans inside of a div. The spans contain a mixture of text and emojis and each span has a unique ID.
My end-goal is to add a class to the range of spans I drag/highlight select. This would happen at mouseout I presume.
With my current code, this works fine as long as I mouseout within the container that houses the spans.
However, if I accidentally overdrag and mouseout on the text outside of the box, the final element is undefined, which is something I can easily see an end-user doing on accident.
Is there a way to ensure that even if the user mouse-outs outside of the div that contains the relevant spans, that the only "selections" recognized are the selected elements within the container div?
jQuery(document).ready(function($) {
jQuery(document).on('mouseup', function(e) {
const sel = document.getSelection();
if(jQuery(sel.focusNode.parentElement).hasClass('subword')) {
var lastnodeid = jQuery(sel.focusNode.parentElement).attr('id');
} else if(jQuery(sel.focusNode).hasClass('subemoji')) {
var lastnodeid = jQuery(sel.focusNode).attr('id');
}
if(jQuery(sel.anchorNode.parentElement).hasClass('subword')) {
var firstnodeid = jQuery(sel.anchorNode.parentElement).attr('id');
} else if(jQuery(sel.anchorNode).hasClass('subemoji')) {
var firstnodeid = jQuery(sel.anchorNode).attr('id');
}
console.log('FIRST NODE ID');
console.log(firstnodeid);
console.log('LAST NODE ID');
console.log(lastnodeid);
$("#"+firstnodeid).nextUntil("#"+lastnodeid).addClass("subselected");
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div style="padding:10px;background-color:red" id="maindiv">
<div style="padding:10px;background:green" id="innerbox">
<span>If accidentally mouseout on this text, no good as the final "selection" will be undefined so I can't get the correct range..</span>
<div style="padding:10px; border: solid 1px #333333;width:100px;background-color:white" class="subbox">
<span id="item1" class="subword">Drag</span>
<span class="swspc" data-type="space"> </span>
<span id="item2" class="subword">Select</span>
<span class="swspc" data-type="space"> </span>
<span id="item3" class="subemoji"><img style="width:10px" src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Ftse1.mm.bing.net%2Fth%3Fid%3DOIP.PwF3M5BEW5hKPHFvl2l0ogHaHa%26pid%3DApi&f=1&ipt=1eb8cf6043603bf3df6cccf015fe4c37ebae980a8ad6c9ac7e04ebf8c7f6bad0&ipo=images"></span>
<span class="swspc" data-type="space"> </span>
<span id="item4" class="subword">These</span>
</div>
</div>
</div>
I'd like to insert an input in an adjacent div where the JS function has been fired.
JavaScript: to add an element (input in this case)
jQuery: to detect where the javascript function has been fired.
The issues I'm facing :
The input is created after the second click.
All the inputs move from one div to another on each click ...
I don't understand why this happens! Do you guys have an idea why everything messes up? Thanks a lot.
var ct = 0; // numeric identifier for training section
var lec = 0; // numeric identifier for lectures
function addLecture() {
lec++;
var div = document.createElement('div');
div.setAttribute('id', 'lecture'.concat(lec))
var input = document.createElement('input');
lecture.setAttribute('type', 'text')
div.appendChild(input)
var id;
jQuery('.info_container').click(function(id) {
var id = jQuery(this).attr("id");
document.getElementById(id).querySelector('[id ^= "lectures_container"]').insertAdjacentElement('beforeend', div);
});
}
[id^="row"] {
padding: 10px;
margin: 10px;
background-color: gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<div id="row-4" class="info_container">
<div id="lectures_container4">
</div>
<a class="btn btn-add" href="javascript:addLecture()"><span class="fa fa-plus"></span> Add an input</a>
</div>
<div id="row-5" class="info_container">
<div id="lectures_container5">
</div>
<a class="btn btn-add" href="javascript:addLecture()"><span class="fa fa-plus"></span> Add an input</a>
</div>
Full code and live example in the JS fiddle right here: https://jsfiddle.net/t7x350d9/
Your code is much more complicated than it needs to be.
Firstly when dealing with repeated HTML structures do not use id attributes. Use the same class on them all to group them. If you need to identify each one, listen for the event and use the target to determine which element was interacted with. In the example below this can be done through the target property of the event which is raised.
Secondly, to achieve your goal simply use an unobtrusive event handler in your JS (not an onclick attribute in the HTML) and append() the new HTML. Try this:
jQuery($ => {
$('.info_container .btn').on('click', e => {
e.preventDefault();
$(e.target).closest('.info_container').find('.lectures_container').append('<div class="lecture"><input type="text" /></div>');
});
});
.info_container {
padding: 10px;
margin: 10px;
background-color: gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<div class="info_container">
<div class="lectures_container"></div>
<a href="#" class="btn btn-add">
<span class="fa fa-plus"></span> Add an input
</a>
</div>
<div class="info_container">
<div class="lectures_container"></div>
<a href="#" class="btn btn-add">
<span class="fa fa-plus"></span> Add an input
</a>
</div>
Recently i tried to create dynamic elements and try to delete them. the problem is, every time i want to delete a specific element, the one get remove is the very first created element. i already try to use live() and on() method too. any idea why is this happen? thanks for help
<body>
<h2>To Do List</h2>
<div id="inputfield">
<input type="text" placeholder="add here" id="input">
<button onclick="addlist()">Add</button>
</div>
<div id="content">
</div>
addlist = () => {
let input = document.getElementById("input").value;
let html = '';
html += `<div style="display: flex; justify-content: center; flex-direction: row" class="main">
<div id="list">
<span id="isilist">` + input + `</span>
</div>
<div id="option">
<button id="deletebtn">delete</button>
</div>
</div>`
$("#content").append(html)
}
$("#content").on('click', 'button', function(){
$("#list").remove()
$("#option").remove()
})
or you can try it on https://jsfiddle.net/tLd3kvyo/
The problem was every time you created a new element, they all have the same id="list" hence when you tried removing it, the browser didn't know which id("#list") you were refering to and hence removed the first one. So,
Please change your delete code to this:
$("#content").on('click', 'button', function(){
$(this).parent().prev().remove();
$(this).parent().remove();
})
This will remove the button's parent and previous element too.
I have a small issue. I have a main category that consists of a row of checkboxes. In this main category, I have a button Add Section, which adds another row of checkboxes under the above row and keep adding another row of checkboxes after each click on the Add Section.
Outside the box, I have a main button that allows adding another main category. The principle is the same, it adds a row of checkboxes, followed by another section button. And if I want to add another row again in this category, I just click on the Add Section button.
The issue is I am having, I am unable to add another row on clicking the Add section button. Also, I want to validate the as from the second row checkboxes and ownwards, such that if I check the first checkbox in the second row, I should be able to check the first checkbox in the third row.
The first row of checkboxes in the main category is independent of the second row of checkboxes and the onwards rows (that is the rows created on adding a section
I have done the validation as in : https://jsfiddle.net/fL3hekhw/
EDITED
The demo is : https://jsfiddle.net/fL3hekhw/
When I click on Add Section button in a box, it should not duplicated the added checkboxes in other boxes. How can I prevent this ? Please help.
Can someone please help me with this ? I am totally stuck.
There are a few issues with the demo you linked to.
You were asking for .closest('.js-cars-items') but you only used .js-cars-item as a class
When you added new checkboxes you were re-using ids, which must be unique across the whole document
When you were adding new categories, you were not registering the 'change' event listener with the new .cars element that you appended
I've made a few minor refactorings that I hope make the code a bit easier to read. I also removed the troublesome .closest('.js-cars-items') call completely because you were already iterating over .js-cars-item anyway.
I'm only unsure about one thing, though. Do you want checkboxes in a column to be validated across all categories or per category?
let nextSection = 4;
const carsItemSelector = '.js-cars-item [type="checkbox"]';
function registerValidation() {
$(".main").on("change", carsItemSelector, validateCategorySelection);
}
function validateCategorySelection() {
const idx = $(this).closest('li').index(); //Get the index - Number in order
const chk = $(this).is(":checked"); //Get if checked or not
const obj = this; //Checkbox object
$('.js-cars-item').each(function() { //Loop every js-cars-item
//Find the checkbox with the same index of clicked checkbox. Change disabled property
$(this).find('li:eq(' + idx + ')')
.find('[type="checkbox"]')
.not(obj)
.prop("checked", false);
});
var checkeds = [];
$(".cars-item input:checkbox:checked").each(function(index) {
checkeds[index] = $(this).attr('id');
});
console.clear();
console.log("These are checked:", checkeds);
}
function checkbox(index, section) {
return `
<li>
<input type="checkbox" id="car-${index}-${section}">
<label for="car-${index}-${section}"><i class="icon-tick"></i></label>
</li>
`;
}
registerValidation();
$('.js-add-category').click(function() {
const section = nextSection;
nextSection++;
var categoryContent =
`<div class="cars">
<div class="cars-item js-cars-item">
<ul>
${[1, 2, 3].map(i => checkbox(i, section)).join('\n')}
</ul>
</div>
<button type="button" class="js-add-section">Add Section</button>
</div>
<br>`
$('.main').append(categoryContent);
// registerValidation();
});
$(document.body).on('click', 'button.js-add-section', function() {
const section = nextSection;
nextSection++;
var sectionContent =
`<div class="cars-item js-cars-item">
<ul>
${[1, 2, 3].map(i => checkbox(i, section)).join('\n')}
</ul>
</div>`
$(this).closest('.cars').append(sectionContent);
});
.cars-item {
border-bottom: 1px solid gray;
}
ul {
/* Added to fully show console in snippet */
margin: 2px;
}
li {
display: inline;
}
.cars {
box-sizing: content-box;
width: 250px;
height: auto;
padding: 10px;
border: 5px solid blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button" class="js-add-category">Add Category</button> <br> <br>
<div class="main">
<div class="cars">
<div class="cars-item js-cars-item">
<ul>
<li>
<input type="checkbox" id="car-1-3">
<label for="car-1-3"><i class="icon-tick"></i></label>
</li>
<li>
<input type="checkbox" id="car-2-3">
<label for="car-2-3"><i class="icon-tick"></i></label>
</li>
<li>
<input type="checkbox" id="car-3-3">
<label for="car-3-3"><i class="icon-tick"></i></label>
</li>
</ul>
</div>
<button type="button" class="js-add-section">Add Section</button>
</div>
<br>
<br>
I'm not sure but I suspect the problem comes from not catching the click event on created buttons, right?
If you want to listen to events on elements that are added after document load, don't use .change() but use $(document).on('eventName', 'elementName', callbackFunction);
Read more here:
Event handler not working on dynamic content
I need to evaluate the contents of a hidden input field, who's value changes depending on the contents of an unordered list. For reference, this input field is part of a Tag-it! Single Input Field(2) assembly.
The problem with evaluating this sort of field is that onchange does not seem to fire on the input element, because the element is not in focus. That means I have two possible directions:
Figure out how to make the onchange event fire when the element isn't in focus;
Evaluate the contents of the unordered list.
The latter of these options seems the most appropriate, however it might also be heavy and possibly inaccurate. Which of these approaches should I be going for?
HTML:
<div id="Likes">
<div class="form-modal-back">
<div class="form-modal-title">
<h4>Choose some things you like</h4>
</div>
<div class="form-modal-main">
<h5>Start by typing a few letters, and choose from the suggestions</h5>
<div class="gap20"></div>
<div class="tagBox">
<input id="likesTags" name="tags" />
</div>
...
<a href="#Dislikes">
<input class="form-modal-button-right hiddenLink" id="onb5" type="button" value="Next" />
</a>
...
HTML at runtime:
<input id="likesTags" name="tags" class="tagit-hidden-field">
<ul class="tagit ui-widget ui-widget-content ui-corner-all">
<li class="tagit-new">
<input class="ui-widget-content ui-autocomplete-input" autocomplete="off" type="text">
<span role="status" aria-live="polite" class="ui-helper-hidden-accessible">1 result is available, use up and down arrow keys to navigate.</span>
</li>
</ul>
JS:
$("#likesTags").onchange(function () {
var text = $("#likesTags").val();
if (text.length > 2)
{
pageCompletionSequence += "1";
checkCompletion("#onb5", "#Dislikes", "11");
}
else
{
pageCompletionSequence = "1";
elementRestore("#onb5");
}
})
function checkCompletion(id, np, seq) {
if (pageCompletionSequence == seq) {
$(id).removeClass("hiddenLink");
$(id).parent().attr("href", np);
pageCompletionSequence = "1";
}
}
function elementRestore(id) {
$(id).addClass("hiddenLink");
$(id).parent().removeAttr("href");
}
This plugin has event callbacks for this case.
$("#likesTags").tagit({
//This should be appended to your current tagit initializing call.
afterTagAdded: function() {
onTagsUpdated('#likesTags');
},
afterTagRemoved: function() {
onTagsUpdated('#likesTags');
}
});
function onTagsUpdated(id){
var tags = $(id).tagit("assignedTags"); //Returns an array of the text values of all the tags currently in the widget.
console.log('tags of',id,'updated:',tags);
}