Input value of a cloned div not changing or firing events - javascript

I have a mock hidden div that I edit, clone and show to users. In this div is an input for which I set the value attribute using jQuery (tried with probably all methods). After I generate the new div and show it to users, the input does not fire any kind of events (focus, focusout, change) neither does the value of input change in HTML.
What am I missing or doing wrong?
Edit (code): HTML
<div class="item-wrapper" id="mock-item">
<div class="item-img"></div>
<div class="item-primary">
<p class="item-name">Foo Bar</p>
<input type="text" class="set-amount">
</div>
</div>
JS:
$("div.item-wrapper").click((e) => {
let item_id = e.currentTarget.id
let itemDetails = {
item_name: $(`#${item_id} .item.name`).text(),
suggested_amount: $(`#${item_id} #item-amount`).text(),
icon_url: $(`#${item_id} .item.big`).attr("style"),
}
$(".item-img", tmp).attr("style", itemDetails.icon_url)
$("#mock-item .item-name").text(itemDetails.item_name)
$("#mock-item .set-amount")[0].setAttribute("value", itemDetails.suggested_amont)
$("#mock-item").clone().removeAttr("id").appendTo(".item-wrapper").show()
})

Cloning an element removes all the event listeners from it. If you would like to listen for all event listeners on elements with a given selector, you can use jQuery's .on:
$(document).on("click", ".class", function () {
const $this = $(this);
});
You could also set the withDataAndEvents parameter on .clone to true: .clone(true).

Related

When cloning an input, only one of the inputs works with an "oninput" function, while the other does not

I have an ol element with some li's. Inside each li is also an input with an oninput function. When the li is clicked on, it's cloned and added to a separate column. The cloning works fine, except for the inputs. Only one of the inputs seems to actually read any values once it's cloned (I assume it's the original).
Any help would be greatly appreciated.
let exerciseBuilder = document.getElementById("exercise-builder");
let exerciseList = document.getElementById("list-of-exercises");
let repsLabel = document.querySelectorAll(".reps-label");
let ETA = document.getElementById("ETA");
// $(".reps").hide();
$(repsLabel).hide();
// This is the function that clones and copies over the li Element. I assume the problem is somewhere in here.
function addToBuilder(e) {
let target = e.target;
if (target.matches('li')) {
exerciseBuilder.insertBefore(e.target, exerciseBuilder.firstChild);
let clone = e.target.cloneNode(true)
exerciseList.appendChild(clone)
$(e.target.children).show();
}
}
// This is the function simply removes the li Element from the right column. This seems to work OK.
function removeFromBuilder(e) {
let target = e.target;
if (target.matches('li')) {
e.target.remove();
}
}
// This is the oninput function that I have linked in the HTML of the very first input.
function estimatedTimeOfCompletion() {
let repsValue = document.getElementById("back-squat-reps").value;
console.log(repsValue);
}
exerciseList.addEventListener("click", function(e) {
addToBuilder(e);
})
exerciseBuilder.addEventListener("click", function(e) {
removeFromBuilder(e);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- Here is the start of the left column, where the input seems to work correctly. -->
<div id="list-of-exercises-container">
<ol id="list-of-exercises">
<li class="exercise" id="back-squat">Back Squat
<!-- I have have the oninput function on one of the inputs at the moment for testing purposes, but the plan is to put on all. -->
<input class="reps" type="text" id="back-squat-reps" oninput="estimatedTimeOfCompletion()"><label class="reps-label">Reps</label>
</li>
<li class="exercise" id="power-clean">Power Clean
<input class="reps"><label class="reps-label">Reps</label>
</li>
<li class="exercise" id="bench-press">Bench Press
<input class="reps"><label class="reps-label">Reps</label>
</li>
<li class="exercise" id="box-jump">Box Jump
<input class="reps"><label class="reps-label">Reps</label>
</li>
</ol>
</div>
<!-- Here is the start of the right column. When the li is cloned over here, the input stops working. -->
<div id="exercise-builder">
<ul>
</ul>
<p id="ETA">Estimated Time Of Completion: </p>
</div>
This is by design per the MDN documentation. cloneNode() does not copy event handlers.
You'll have to add the event listener to each element created.
let clone = e.target.cloneNode(true)
clone.addEventListener("click", <function>);
exerciseList.appendChild(clone)
You can pass the event object to the input event handler and read the value of the current <input> with e.target.value (alternatively, you could pass the element itself to the function with this). However, you should consider using addEventListener instead of inline event handlers.
<input class="reps" type="text" oninput="estimatedTimeOfCompletion(event)">
function estimatedTimeOfCompletion(e) {
let repsValue = e.target.value;
console.log(repsValue);
}
It would be better to give the <input> a specific class and use event delegation. Note that cloning an element with the id attribute causes there to be duplicate ids in the document.

Use XPath or onClick or onblur to select an element and use jQuery to blur this element

*UPDATE:I am new to jQuery, as well as using XPath, and I am struggling with getting a proper working solution that will blur a dynamically created HTML element. I have an .onblur event hooked up (doesn't work as expected), and have tried using the $(document.activeElement), but my implementation might be incorrect. I would appreciate any help in creating a working solution, that will blur this element (jqInput) when a user clicks anywhere outside the active element. I have added the HTML and jQuery/JavaScript below.
Some ideas I have had:
(1) Use XPath to select a dynamic HTML element (jqInput), and then use jQuery's .onClick method to blur a this element, when a user clicks anywhere outside of the area of the XPath selected element.
(2) Use the $(document.activeElement) to determine where the .onblur should fire:
var thisTitle = input0;
var activeElement = $(document.activeElement);
if (thisTitle != activeElement) {
jqInput.hide();
_layout.viewHeaderTextInput.inputOnBlurHandler(canvasObj, jqHeaderText, jqInput);
}
I am open to all working solutions. And hopefully this will answer someone else's question in the future.
My challenge: Multiple elements are active, and the .onblur does not fire. See the image below:
NOTE: The <input /> field has focus, as well as the <div> to the left of the (the blue outline). If a user clicks anywhere outside that <input />, the blur must be applied to that element.
My Code: jQuery and JavaScript
This is a code snippet where the variable jqInput and input0 is created:
var jqInput = null;
if (jqHeaderText.next().hasClass("inline-editable"))
{
//Use existing input if it already exists
jqInput = jqHeaderText.next();
}
else
{
//Creaet a new editable header text input
jqInput = $("<input class=\"inline-editable\" type=\"text\"/>").insertAfter(jqHeaderText);
}
var input0 = jqInput.get(0);
//Assign key down event for the input when user preses enter to complete entering of the text
input0.onkeydown = function (e)
{
if (e.keyCode === 13)
{
jqInput.trigger("blur");
e.preventDefault();
e.stopPropagation();
}
};
This is my .onblur event, and my helper method to blur the element:
input0.onblur = function ()
{
_layout.viewHeaderTextInput.inputOnBlurHandler(canvasObj, jqHeaderText, jqInput);
};
inputOnBlurHandler: function (canvasObj, jqHeaderText, jqInput)
{
// Hide input textbox
jqInput.hide();
// Store the value in the canvas
canvasObj.headingText = jqInput.val();
_layout.updateCanvasControlProperty(canvasObj.instanceid, "Title", canvasObj.headingText, canvasObj.headingText);
// Show header element
jqHeaderText.show();
_layout.$propertiesContent.find(".propertyGridEditWrapper").filter(function ()
{
return $(this).data("propertyName") === "Title";
}).find("input[type=text]").val(canvasObj.headingText); // Update the property grid title input element
}
I have tried using the active element, but I don't think the implementation is correct:
var thisTitle = input0;
var activeElement = $(document.activeElement);
if (thisTitle != activeElement) {
jqInput.hide();
_layout.viewHeaderTextInput.inputOnBlurHandler(canvasObj, jqHeaderText, jqInput);
}
My HTML code:
<div class="panel-header-c">
<div class="panel-header-wrapper">
<div class="panel-header-text" style="display: none;">(Enter View Title)</div><input class="inline-editable" type="text" style="display: block;"><div class="panel-header-controls">
<span></span>
</div>
</div>
</div>
I thank you all in advance.

Not able to track change in input value

In the template I have a input element.
<input class='qq-edit-caption-selector qq-edit-caption kk-editing' placeholder='Enter Caption here ...' onkeypress='captionUpdate();'>
I want to track the change in the input value,and enable the Update button.Tried below options:
onkeypress='captionUpdate();'
Tried jquery change or click on the class
$('.qq-edit-caption').change(function() {
alert('qq-edit-caption');
});
Both options does not get fired up!!not sure I have anything issues with my setup or Fine Uploader does not allow that? Please see my screenshot:
Any way to solve this problem with FU?
If you are simply adding this inline event handler directly to the template element, then it's not surprising that it's never triggered. Fine Uploader templates are quite primitive in that the template is interpreted as an HTML string and then used to create DOM elements inside of your container element (the element referenced as your element option).
You really should never use inline event handlers. There are quite a few disadvantages to this approach. I talk about this in more depth in my book - Beyond jQuery. And the method of attaching event handlers is not necessary at all in your case, as far as I can tell. Instead, after constructing a new instance of Fine Uploader, simply attach an event handler of your choice to the input element using addEventListener. For example if your <input> element is given a CSS class name of 'qq-edit-caption', you can attach a "change" event handler like this:
var uploadContainer = document.querySelector('#my-uploader')
var uploader = new qq.FineUploader({
element: uploadContainer
...
})
uploadContainer.querySelector('.qq-edit-caption')
.addEventListener('change', function(event) {
// event handler logic here...
})
...and if you are creating this input element for each file and need to attach a "change" handler to all of these input elements, you should attach a single delegated event handler to the container element, and react based on the element that initially triggered the event (look at the target property of the event). You can determine the ID of the file by looking at the CSS class of the parent <li> of the event.target, or you can look for a 'qq-file-id' attribute on the parent <li> of the target element (the value will be the file ID). That code might look something like this:
var uploadContainer = document.querySelector('#my-uploader')
var uploader = new qq.FineUploader({
element: uploadContainer
...
})
uploadContainer.addEventListener('change', function(event) {
if (event.target.className.indexOf('qq-edit-caption') >= 0) {
var fileId = parseInt(event.target.getAttribute('qq-file-id'))
// ...
}
})
This might get you started:
$('.inp input').keyup(function(){
if (this.value.length > 0) {
$(this).closest('.row').find('.cell.btn button.upload').prop('disabled', false);
}else{
$(this).closest('.row').find('.cell.btn button.upload').prop('disabled', true);
}
});
* {position:relative;box-sizing:border-box;}
.row{overflow:hidden;}
.cell{float:left;height:40px;}
.pic{width:82px;}
.inp{width:230px;}
.inp input{font-size:1rem;padding:2px 5px;}
.btn{width:60px;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="row">
<div class="cell pic">
<img src="http://lorempixel.com/80/40">
</div>
<div class="cell inp">
<input class='qq-edit-caption-selector qq-edit-caption kk-editing' placeholder='Enter Caption...'>
</div>
<div class="cell btn">
<button class="upload" disabled>Upload</button>
</div>
<div class="cell btn">
<button class="del" disabled>Delete</button>
</div>
</div><!-- .row -->
You can enable and disable the state of button by the input value.Using the Keyup function
$('.qq-edit-caption').keyup(function() {
if(this.value.length > 0){
$("#edit").prop('disabled', false);
}
else {
$("#edit").prop('disabled', true);
}
});
Have you tried the onchange event? Does it work?
<input class='qq-edit-caption-selector qq-edit-caption kk-editing' placeholder='Enter Caption here ...' onchange='captionUpdate();'>

remove onclick when attached to children

I have the following code:
layoutOverlaysBldg = $("#layout-overlays-bldg")
layoutOverlaysBldg.on("click", "div", function(event) {
var floor;
console.log("floornum: " + this.dataset.floornum);
floor = parseInt(this.dataset.floornum);
...
$("#choose-floor").fadeOut();
$("#choose-apt").fadeIn();
});
later - based on data I'm getting back from the DB - I want to remove some of the .on("click", "div", ...) from only some of the divs. I already have the selector that is getting the right divs but I cannot figure out how to remove the click event. I have tried .off("click") after selecting the right div but it has no effect.
This issue here is because you are using a delegated event. You can add or remove the event for all child elements, but not individual ones given your div selector.
With that in mind the easiest way to do what you need is to add the event based on a class, then add and remove that class on the children as needed. Something like this:
layoutOverlaysBldg = $("#layout-overlays-bldg")
layoutOverlaysBldg.on("click", "div.clickable", function(event) {
// your code...
});
You can then enable/disable the event on the child div by adding or removing the .clickable class.
You can try like this :
Example :
<div id="test">
<div id="first">first</div>
<div id="second">second</div>
</div>
<div onclick="unbindSecondDiv();">UNBIND</div>
<script>
function unbindSecondDiv()
{
test = $("#second")
test.unbind("click");
alert('Selected Area Click is Unbind');
}
$(document).ready(function(){
//BIND SELECTED DIV CLICK EVENT
test = $("#test > div")
test.bind("click" , function(event) {
alert($(this).attr('id'));
});
});
</script>
In the above example , selected DIV elements click event is bind.
And after execute function unbindSecondDiv() , second DIV click event will be unbind.
Have a try , may helps you.

JS Events not attaching to elements created after load

Problem: Creating an Element on a button click then attaching a click event to the new element.
I've had this issue several times and I always seem to find a work around but never get to the root of the issue. Take a look a the code:
HTML:
<select>
<option>567</option>
<option>789</option>
</select>
<input id="Add" value="Add" type="button"> <input id="remove" value="Remove" type="button">
<div id="container">
<span class="item">123</span>
<br/>
<span class="item">456</span>
<br/>
</div>
JavaScript
$(".item").click(function () {
if ($("#container span").hasClass("selected")) {
$(".selected").removeClass("selected");
}
$(this).addClass("selected");
});
$("add").click(function() {
//Finds Selected option from the Select
var newSpan = document.createElement("SPAN");
newSpan.innerHTML = choice;//Value from Option
newSpan.className = "item";
var divList = $("#container");
divList.appendChild(newSpan);//I've tried using Jquery's Add method with no success
//Deletes the selected option from the select
})
Here are some methods I've already tried:
Standard jQuery click on elements with class "item"
Including using the `live()` and `on()` methods
Setting inline `onclick` event after element creation
jQuery change event on the `#Container` that uses Bind method to bind click event handler
Caveat: I can not create another select list because we are using MVC and have had issues retrieving multiple values from a list box. So there are hidden elements that are generated that MVC is actually tied to.
Use $.on instead of your standard $.click in this case:
$("#container").on("click", ".item", function(){
if ( $("#container span").hasClass("selected") ) {
$(".selected").removeClass("selected");
}
$(this).addClass("selected");
});
It looks to me like you want to move the .selected class around between .item elements. If this is the case, I would suggest doing this instead:
$("#container").on("click", ".item", function(){
$(this)
.addClass("selected")
.siblings()
.removeClass("selected");
});
Also note your $("add") should be $("#add") if you wish to bind to the element with the "add" ID. This section could also be re-written:
$("#add").click(function() {
$("<span>", { html: $("select").val() })
.addClass("item")
.appendTo("#container");
});

Categories

Resources