jQuery .html() not setting html at all - javascript

I have a simple thing. When a user clicks on the edit link it turns the previous element into an input element for editing and the edit into a cancel. If the user decides not to edit he can click on cancel and everything should revert to its initial state.
Right now this is not working at all:
$('.cancel').on('click', function() {
$(this).parent().html("<a href='#'>edit</a>");
});
HTML:
<div class='photo-section'>
<div class='photo-head'>
<div class='photo-info'>
Photo Name : <span class='photo-name'>Work selfie</span>
<span class='title-edit'><a href='#'>edit</a></span>
</div>
</div>
<div class='photo'>
<img src='' alt='' title=''>
</div>
<div class='tag-section'>
<div class='tags'>Photo Tags:
<span>#code#coffee#late#night</span>
<span class="tags-edit">edit</span>
</div>
</div>
</div>
CSS:
// JavaScript to handle photo operations
$(document).ready(function() {
// show/hide edit option
$('.photo-info, .tags').hover(function() {
$(this).find('.title-edit > a, .tags-edit > a').addClass('visible');
}, function() {
$(this).find('.title-edit > a, .tags-edit > a').removeClass('visible');
});
// show editable area
$('.title-edit, .tags-edit').on('click', function () {
edit(this);
});
});
function edit(elem) {
// change element into an input elemnt for editing
var $item = $(elem).prev();
var text = $item.text();
$item.html("<input type='text'>").find('input').attr('value', text);
// change edit to cancel if input element present
if ($('input').length) {
$item.next().html("<a href='#' class='cancel'>cancel</a>");
}
// change cancel back to edit
if ($('.cancel').length) {
$('.cancel').on('click', function() {
$(this).parent().html("<a href='#'>edit</a>");
});
}
}
Result: https://jsfiddle.net/hgwkxygz/6/
Any help would be great!

This is a very common case of attaching event at wrong time in javascript.
Actually you are removing and re-adding a DOM element. So the already attached event to .cancel won't work this time. You again have to write event listener on .cancel whenever you attach a new DOM element after clicking on edit button.
Basically it means whenever you do .html(), you have to re-add event listener for click.
There are various approach to solve this problem.
1) make a function which attach the DOM element as well as click event to that DOM element. Call that function only on click events.
2)Do event delegation.
3)Do not remove DOM elements on click events, rather hide and show elements so that you wont loose your event listeners.
4)If you really have to do remove and re-add DOM elements then in my opinion, best approach is to make a class, where you make DOM elements, add event listeners privately in that class, and on click events just make new instance of that class.
You can check out these options in detail on web.

Related

Trying to add EventListener to dynamically created object

I am attemping to load HTML from an external file. However upon doing so the input elements are non-interactable. I have tried this using vanilla JavaScript to no avail and just imported jQuery. My current progress is as follows:
I have a page with a static element and a menu bar. When clicking on the menu bars icons the elements content gets updated through JS/JQ. This is my HTML and jQuery for loading the view:
settings.html:
<section class="settings">
<div class="container">
# some elements here
<div id="graph-settings" class="settings-card">
<i class="fa fa-chart-line fa-3x"></i><h1>Graph Settings</h1>
<p>Web Interface Refresh Rate</p>
<form>
<input type="text" name="graphInterval" id="graphInterval" placeholder="E.g. 2000">
</form>
</div>
# more elements here
</div>
</section>
jQuery:
$.get("./pages/settings.html", (data) => {
$("#main").append(data);
});
$(document).on("click", "#graphInterval", function() {
// do something...
console.log("test");
});
The content seems to be loaded correctly into the page but is not markable/interactable (dynamically added to DOM etc.). However my jQuery does not seem to find the #graphInterval element as I get no logged output from the console.
Any way to get the input fields working would be a solution. All they're needed for is to edit and retrieve it's value. I will use JavaScript to add/interact with buttons later on, no posting forms will be used, hence why the form has no "action=''".
If you use event delegation (where you set the event listener at a high level DOM object and let all events triggered from decedents of that high level element bubble up to the high level element, any new elements added in will trigger events caught higher up. Then, in the listener, you can check to see which element actually triggered the event and act accordingly. So, in your case, remove the id from the .on() method call and then check the event.target inside the listener:
// Set the event listener on a high level element and capture the event
$(document).on("click", function(event) {
// You can find out what exact element triggered the event with event.target
console.log("event triggered by: " + event.target);
// Now you can proceed as you need to:
if(event.target.id === "heading1"){
console.log("You clicked the heading");
} else if(event.target.id === "para1"){
console.log("You clicked the paragraph");
}
});
// Create new elements
let data1 = "<h1 id='heading1'>Some new data (click me)</h1>";
let data2 = "<p id='para1'>Some new data (click me)</p>";
// Dynamically add elements to document
$(document.body).append(data1);
$(document.body).append(data2);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

How to change image when two buttons are clicked?

I want to make jQuery script where I will have 10 buttons all with different colors and after user clicks on two different buttons, combination of the clicked buttons colors will be made and switch the image frame with already prepared images based on color combinations.
My question is how to put conditions for two buttons(or links) clicked.
switch me
<img src="http://placehold.it/333/fe3/img/picture2.jpg" id="bg" />
$(function() {
$('.menulink').click(function(e){
e.preventDefault();
$("#bg").attr('src',"http://placehold.it/333/3ef/img/picture1.jpg");
});
});
I want to achieve something like this but I want image to change when two buttons (or links) are clicked.
http://jsfiddle.net/maniator/Sevdm/
provided you wanted to make sure the buttons were unique when clicked, you'll want a way of tracking which have been seen. I'm using a JS Object like a Set here for compat with older browsers.
var clickCount = 0;//count the clicks
var clickTracker = {};//track which id's were clicked
var clickThreshold = 2;//the number of clicks we want before executing the if block
function clickHandler(){//callback function for the event
if(clickTracker[this.id] === undefined){//we haven't seen this id yet
clickCount++;//increment the number of buttons clicked
clickTracker[this.id] = 1;//flag for tracking the click
if(clickCount >= clickThreshold){//we saw at least clickThreshold clicks
console.log(clickCount + 'unique clicks happened!');
//your work here
}
}
}
$('.cls').click(clickHandler);//bind events
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id='btn1' class='cls'>1</button>
<button id='btn2' class='cls'>2</button>
<button id='btn3' class='cls'>3</button>
<button id='btn4' class='cls'>4</button>
<button id='btn5' class='cls'>5</button>
You can use event delegation to track clicks on .menulink elements. using e.delegateTarget we can have the parent container hold a variable that determines if a .menulink element has been clicked previously, and if it has, we can have it change the background.
To do this I switched the click method to an on method. The on methods parameters are:
$(element).on(event, delegated selector, function)
JQuery .on documentation
Delegation simply means that instead of checking if each button has been clicked by placing an event on the button, we place an event on the container of the buttons and ask if the element that caused the event matches our delegated selector. If it does the provided function is fired. To access the parent of the delegated element within the code we can use the passed back event object, or in this case e.delegateTarget
JQuery delegateTarget documentation
Since everything in JavaScript is an Object, we can assign new properties and methods to anything that is not explicitly part of the Browser's most base architecture. This absolutely includes the body of a page.
In the code below, I place a new property clicked on the body of the page. This is added on click of the first .menulink element, and is checked upon each subsequent click.
Because of this we can know if it is a second click by determining if body.clicked is true. If it is we tell the script to change the background image.
$(function() {
$('body').on("click", ".menulink", function(e){
if(e.delegateTarget.clicked) $("#bg").attr('src',"http://placehold.it/333/3ef/img/picture1.jpg");
else e.delegateTarget.clicked = true;
e.preventDefault();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
switch me
switch me
<img src="http://placehold.it/333/fe3/img/picture2.jpg" id="bg" />
This is what I was looking for
<script>
$(document).ready(function() {
var chosenColors = [];
$(".colors button").click(function() {
chosenColors.push($(this).text())
check();
});
function check() {
if (chosenColors.length === 2) {
$(".pretty-picture").attr("src", chosenColors.sort().join("-") + ".png")
chosenColors = [];
}
}
});
</script>
<body>
<div class=colors>
<button type="button">red</button>
<button type="button">yellow</button>
<button type="button">blue</button>
</div>
<img class="pretty-picture">
</body>

Event fails to fire after DOM change in jQuery 1.9

I am trying to code a cancel button for user input. The user will be able to edit an item after double-clicking on it and the cancel button will allow the user to cancel the action.
The double-clicking part of the code works great, as a text-input box appears with cancel button attached. But now since the DOM has changed, jQuery no longer select the new element, and therefore when the cancel button is clicked the event is not fired. To illustrate, the code are following:
<div id="main">
<ul class="todoList">
<li id="todo-1" class="todo">
<div class="text">New Todo Item. Doubleclick to Edit
</div>
<div class="actions"> Edit</div>
</li>
</ul>
</div>
var currentTODO;
$('.todo').on('dblclick', function () {
$(this).find('a.edit').click();
});
$('.todo a').on('click', function (e) {
e.preventDefault();
currentTODO = $(this).closest('.todo');
});
$('.todo a.edit').on('click', function () {
var container = currentTODO.find('.text');
if (!currentTODO.data('origText')) {
// Saving the current value of the ToDo so we can
// restore it later if the user discards the changes:
currentTODO.data('origText', container.text());
} else {
// This will block the edit button if the edit box is already open:
return false;
}
$('<input type="text">').val(container.text()).appendTo(container.empty());
container.append(
'<div class="editTodo">' +
'<a class="cancel" href="#">Cancel</a>' +
'</div>');
});
// The cancel edit link:
$('.cancel').on('click', function () {
alert("oops");
});
Or here: http://jsfiddle.net/S7f83/42/
My question is therefore, how can I 'bind' the event after DOM has changed? Thank you very much
In your example the events are bound to the controls which exist at the moment of binding, and match the selector. If you would like the actions to apply for newly created controls, you shall bind the event to the document itself. As it is the highest level DOM element, all the events from lower levels will propagate up to it.
At the second parameter you give the context, so only if the event is received from that context will the function fire.
$(document).on('dblclick', '.todo', function () {
$(this).find('a.edit').click();
});
I won't cite your whole code, but you will get the idea from this one.
Binding listeners to the document is encouraged as it doesn't create as many bindings as many controls you have, just one top level binding, which waits for events to propagate up on the DOM tree. More info on optimalization: http://24ways.org/2011/your-jquery-now-with-less-suck/
Use
$('.todo').on('click', '.cancel', function () {
alert("oops");
});
so you delegate the event to an existing element (the .todo)
instead of
$('.cancel').on('click', function () {
alert("oops");
});
2 Methods
Bind the event to the cancel button after it is created http://jsfiddle.net/S7f83/44/
Bind a live (delegated) event to the cancel button http://jsfiddle.net/S7f83/43/
In the 2nd case, I bind the event delegation to the nearest parent, so the event doesn't need to bubble to the document element to be executed. This could improve some performance. It depends on the use whether you want to place the cancel button anywhere else or not.
EDIT: each li has a different id, so it's better to attach to the class .todo
$('.todo').on('click', 'a.cancel', function () {
alert("oops");
});

jQuery won't bind click to underscore template using .on() for items added via function

I'm using underscore to create some elements and appending them to a div with jQuery.
At the bottom of the page I'm using jQuery's .on() to respond to clicks on the elements.
$('.pickup').on('click',
function(e) {
alert("hello");
}
);
Via some user interaction (in Google maps), I've got to add more elements to the div and want them to respond to clicks as well. For some reason they do not. I've pared it all down on jsfiddle:
http://jsfiddle.net/thunderrabbit/3GvPX/
When the page loads, note that clicking on the lines in output will alert('hello') via jQuery.
But click the [add] button and the new lines do not respond to clicks.
My HTML
<div id="unit_2225" class="pickup">
<span>Click me; I was here first</span>
</div>
<script type="text/template" id="unit-template">
<div class="unit-item">
<span class="pickup">
<span>click us (<%= unit_id %>) via underscore</span>
</span>
</div>
</script>
<div id="divID">
</div>
<button>add</button>
My Javascript
var addUnitToDiv = function(key,val) {
console.log(val);
var template = _.template($('#unit-template').html(),val);
$('#divID').append(template);
}
var unit_ids = [{unit_id:'hello'},
{unit_id:'click'},
{unit_id:'us'},
{unit_id:'too'},
{unit_id:112}];
$.each(unit_ids, addUnitToDiv);
var unit_pids = [{unit_id:'we'},
{unit_id:'wont'},
{unit_id:'respond'},
{unit_id:'to'},
{unit_id:'clicks'},
{unit_id:358}];
createMore = function() {
$.each(unit_pids, addUnitToDiv);
}
$('.pickup').on('click','span',function() {
alert("hello");
});
$('button').click(createMore);
I found a similarly worded question but couldn't figure out how to apply its answer here.
Instead of binding events directly to the elements, bind one event to their container element, and delegate it:
$("#divID").on("click", ".pickup", function () {
// Your event handler code
});
DEMO: http://jsfiddle.net/3GvPX/3/
In this case, the event handler is only executed for elements inside of the container #divID that have the class "pickup".
And in your scenario, the elements are being added to the element with an id of "divID". Thus, where the two selectors came from.
This is handy because, as you've found out, dynamically adding elements doesn't magically bind event handlers; event handlers bound normally with .on() are only executed (bound) on those present at the time of binding.
It could even help if you change the delegated selector to "span.pickup" (if you know the elements will always be a <span> like in your template), so that the DOM is filtered by the tag name first.
Reference:
http://api.jquery.com/on/#direct-and-delegated-events
Working demo http://jsfiddle.net/u2KjJ/
http://api.jquery.com/on/
The .on() method attaches event handlers to the currently selected set of elements in the jQuery object. You can attach the handler on the document level.
Hope it fits the need, :)
code try the code changed below
$(document).on('click','.pickup',function() {
alert("hello");
});

Prevent default function using only child pseudo selectors

I am using jQuery to dynamically add elements every time a user clicks a link (".add-event"). Also included in this element is a icon to remove the entire element if the user chooses. I want to hide or disable this remove icon if the element is the last left on the page. This is what I have tried so far:
$(document).on('click','.close_box2:only-child',function(){
event.preventDefault();
});
AND
if( $('.event').length===1) {
$('.close_box2').hide()
}
HTML:
<div class="event">
<span class="close_box2"><i class="icon-remove"></i></span>
<span class="add-event">Add Event</span>
</div>
What am I doing wrong!? Thanks
JSFiddle: http://jsfiddle.net/TrueBlueAussie/KPY9r/6/
You just want to check for the "last one" inside the delete handler:
// initial hide of sole close button
$('.close_box2').hide();
// On add, clone the template and show all close buttons
$(document).on('click', '.add-event', function () {
// Create a new event based on the template
$('#events').append($('#template').html());
// Show all close buttons (as we must now have > 1 event)
$('.close_box2').show();
});
$(document).on('click', '.close_box2', function (e) {
$(this).closest('.event').remove();
// Is there only one left?
if ($('.event').length === 1) {
// Hide the close box on the last event
$('.close_box2').hide()
}
});
Notes:
I use a dummy <script> element to hold you HTML template. This is better than cloning an element on the page and much better than inline HTML strings in your code.
The following looks better regarding performance:
$('.close_box2:only-child').on('click', function(event){
event.preventDefault();
});

Categories