I am currently learning backbone.js and am wondering about removing an item from a series. I am working on the infamous recipe app and am creating step by step directions within textareas. I want to be able to remove one textarea without removing all of them. the creation event is like this:
"click #btnAddDirection": "addNewDirection"
addNewDirection: function (ev) {
ev.preventDefault();
this.directionCount++;
this.render(this.directionCount);
},
and my remove looks like this:
'click .subDirection': 'subDirection'
subDirection: function () {
this.$el.remove();
}
the issue I have is that when I click the [-] button all of the directions are removed rather than a single one. I am thinking I need to pass some sort of identifier in for the direction. Any help would be appreciated. Thanks.
here is the entire Direction Control View:
// The Direction Control View
var DirectionControl = Backbone.View.extend({
render: function (directionCount) {
if (directionCount == null) {
directionCount = 1;
}
var that = this;
var directions = new Directions();
var completeDirections = _.invoke(directions, 'fetch');
$.when.apply($, completeDirections).done(function () {
var template = _.template($('#direction-control-template').html(), { directions: directions.models, directionCount: directionCount });
$('.tblDirections').each(function () {
$(this).find('.addDirection').remove();
$(this).find('.newDirection').text('');
});
$(that.$el).append(template);
});
},
events: {
"click #btnAddDirection": "addNewDirection",
'click .subDirection': 'subDirection'
},
directionCount: 1,
addNewDirection: function (ev) {
ev.preventDefault();
this.directionCount++;
this.render(this.directionCount);
},
subDirection: function () {
this.$el.remove();
}
});
A Backbone best practice in this type of situation would be to give each direction its own individual View. Then, if you create an event within the Direction view, it will only trigger on elements within that view.
On your callback after the directions are fetched, you could try to loop through each in directions and create a new DirectionView view:
DirectionView = Backbone.View.extend({
events: {/*put click events for the individual direction here*/}
render: function(){/*render to this.el */ return this}
})
after you create the new DirectionView, you can render it to the main view you already have:
$(that.$el).append(directionView.render().el);
Your subDirection function will receive an event object in its parameters- you can use this to remove the correct direction. I suggest adding an id for each direction, but if you aren't worried about duplicates you could just pretend the content of the direction is the id. Example:
subDirection: function(event){
var $direction = $( event.currentTarget ).siblings('.direction-container');
var directionId = $direction.text();
// loop through the model's directions until you find the corresponding direction
// then remove that from the directions array
this.render(); // re-render the view
}
This is more of a jQuery-esque solution - not the Backbone best-practice like #Kyle R's, but this will accomplish what you're looking for.
what ended up working was to add data-index to the item in this fashion.
<!--Table for Direction entry control -->
<script type="text/template" id="direction-control-template">
<table class="tblDirections" id="idx_<%= directionCount %>">
<tr>
<td class="Directionlabel">Step # <%= directionCount %><input type="hidden" name="DirectionStep" value="1" />:</td>
<td class="Direction"><textarea id="Direction" rows="5" cols="70"></textarea><br></td>
<td>
<button type="button" id="btnAddDirection" class="btn btn-success btn-xs addDirection">+</button>
<button type="button" id="btnSubDirection" class="btn btn-danger btn-xs subDirection" data-index="idx_<%= directionCount %>">-</button>
</td>
</tr>
</table>
</script>
the control looks like this:
subDirection: function (ev) {
ev.preventDefault();
var currentTarget = $(ev.currentTarget);
var idx = currentTarget.data("index");
console.log("got index of " + idx);
$("#" + idx).remove();
Related
I am populating a data-table from a model using a foreach loop:
#foreach(var item in Model)
{
<tr>
<td style="display: none">#item.Id</td>
<td>#item.Name</td>
<td>#item.Description</td>
<td>
<div class="btn-group">
<button type="button" class="btn btn-default" data-dismiss="modal">Update</button>
<button type="button" data-id=#item.Id id="Delete" class="btn btn-danger" data-dismiss="modal">Delete</button>
</div>
</td>
</tr>
}
Each row of the table has an update and delete button.
I'm struggling to bind the buttons to a click event using jQuery.
Here is my script so far which is within the document ready function:
var table = $('#productTable').DataTable();
$('#productTable tbody').on('click', 'tr', function () {
var data = table.row(this).data();
alert(Product ID = ' + data[0] + ');
//Call delete function and pass in Id
});
This is understandably showing an alert anytime the user clicks the row. How to I get it to only fire when the delete button is clicked?
Thanks in advance for any help.
You can take doutriforce suggestion and bind the events to a class, for example:
$("#productTable tbody").on("click", ".btn-update", function() {
// Your update code here
// Use $(this) to access the button that triggered this event
}
$("#productTable tbody").on("click", ".btn-delete", function() {
// Your delete code here
// Use $(this) to access the button that triggered this event
}
I've used: $("#productTable tbody").on("click", ", function); because it also works for dynamically added elements (in this case, table rows).
Based on the documentation here, it looks like you need to edit the selector that you bind the click event too, like this:
$('#productTable button#Delete').on('click', function () {
var data = table.row(this).data();
alert(Product ID = ' + data[0] + ');
});
const medias = document.querySelectorAll('._23fpc');
for (let i=1; i<medias.length; i++) {
const media = medias[i];
const firstClickable = media.querySelectorAll('._1JX9L')[1];
if(firstClickable) {
window.setTimeout(() => firstClickable.click(), 50);
}
}
I edited the code copied from the internet, it kind of works
PS:I know nothing about coding
Basically, what I am trying to do is create a bbcode editor with a textbox, some buttons and jQuery. Here is my form:
<div class="form-group">
<div class="btn-group btn-group-sm">
<button type="button" class="btn glyphicon bbcode" rel="bold"><b>B</b></button>
<button type="button" class="btn glyphicon bbcode" rel="italic"><i>I</i></button>
</div>
</div>
<div class="form-group">
<textarea class="bbcode" rel="editor" cols="100" rows="12"></textarea>
</div>
and my plugin is called using:
<script>
$('document').ready(function() {
$('.bbcode').bbcode();
});
</script>
and the plugin itself, I am just trying to get the basics done at the minute to update the textbox data when a button is clicked:
(function($) {
"use strict";
$.fn.bbcode = function() {
this.click(function() {
var rel = $(this).attr('rel');
if (rel == 'editor') {
return this;
} else {
alert($(this).attr('rel')); // I can see this pop up so the click event is firing
$('.bbcode[rel=editor]').val('test');
return this;
}
});
}
} (jQuery));
This seems to be the only way I can pick up the textbox, I don't really want to hardcode the class I want like that. I think what I am looking for is a way to get the collection from the function call in the script tags.
This is more than likely something stupid/obvious I have overlooked.
The value of this in the immediate function refers to the collection. However, it is shadowed by the this inside your click handler (which refers to the element being clicked) so you cannot access it.
Create a variable to store this and that'll be your collection.
(function ($) {
"use strict";
$.fn.bbcode = function () {
var $editors = this;
this.click(function () {
var rel = $(this).attr('rel');
if (rel == 'editor') {
return this;
} else {
alert($(this).attr('rel')); // I can see this pop up so the click event is firing
$editors.val('test');
return this;
}
});
}
}(jQuery));
I have a table full of appointments. Every appointment has two buttons. One for canceling the event, one for accepting it.
I am struggling to get the appointmentId in the jQuery function when I click on a button. Can you please give me a hint how to do this? The appointmentId is in the table as a hidden input field.
// my html code
<tr>
<td align="left">
<input type="hidden" name="appointmentId" value="234">
John Smith - 14.03.2013 at 9 o'clock
</td>
<td align="right">
<input type="button" id="acceptEvent" class="acceptEvent" value="Accept">
<input type="button" id="cancelEvent" class="cancelEvent" value="Cancel">
</td>
</tr>
// my jQuery code
$("body").delegate('.acceptEvent', 'click', function() {
console.log('accept event clicked');
// get the appointmentId here
});
$("body").delegate('.cancelEvent', 'click', function() {
console.log('cancel event clicked');
// get the appointmentId here
});
Use closest to grab the parent tr element, then select your hidden field.
The reason that this is the correct answer is because it takes the context of the click event with $(this). Then it travels up the DOM tree to your root table row element and selects the child by name. This ensures that you are always in the correct row.
EDIT: I know you already selected an answer, but this was really bothering me that it wasn't working properly. I had to walk down twice using .children() to get it to work though you could also use .find('input[name="appointmentId"]'). Even though you've already selected your answer, I hope this will help you.
$('.acceptEvent').click(function() {
var myVal = $(this).closest('tr').children().children().val();
});
$('.cancelEvent').click(function() {
var myVal = $(this).closest('tr').children().children().val();
});
In the click function, you have access to the button that was clicked with this so you can do:
$("body").on('click', '.cancelEvent', function() {
var input = $(this).closest('tr').find('input[name="appointmentId"]').val();
});
Assuming you have no other IDs or classes to key off of, you can use jQuery's Attribute Equals Selector in reference to the clicked button's parent tr element:
$('.acceptEvent').click(function() {
// get the appointmentId here
var appointmentId = $(this).closest('tr').find('input[name="appointmentId"]').val();
});
I'll do it like that :
$("body").on('.acceptEvent', 'click', function() {
var id = $('input[name="appointmentId"]').val();
//Or search in the parent <tr>
var id = $(this).parent().find('input[name="appointmentId"]').val();
console.log('accept event clicked');
console.log('Id is ' + id);
});
In MVC, I constructed a table in a view, by iterating over a 'List' of a viewmodel.
#foreach(var item in Model)
{
<tr id="row-#item.name">
<td>
#item.type
</td>
<td >
#Html.ActionLink((string)item.name, "Download", "FileUpload", new { rout = item.rout, fileName = item.name }, null)
</td>
<td>
#item.size
</td>
<td>
Delete
</td>
</tr>
}
when I load the page, I get a list of files, with a 'delete' link for each.
I implemented with jQuery a basic 'remove row' method:
$('#delete-file').click(function() {
var delUrl = $(this).attr('del-url');
$.post(delUrl, null, removeRow(),'json')
});
function removeRow() {
$($('#delete-file').closest('tr')).fadeOut('slow');
}
When I click 'delete' on one of the file rows, it performs well, but then, if I click on another (delete), nothing happens. No file is deleted on server, and the row is not removed, as if it's being ignored completely.
You need to use class="delete-file" instead of id="delete-file" - and of course the corresponding $(".delete-file") selector as well.
IDs are meant to be unique per document, and your code binds the handler to the first id="delete-file" element.
Here is you solution:
http://jsfiddle.net/irpm/enEAt/1/
the javascript is:
$('.delete-file').click(function(e) {
$(this).closest('tr').remove();
});
You need to prevent the default action of the <a> element, and use the correct selectors
$('.delete-file').click(function(e) {
e.preventDefault();
var self=this,
delUrl = $(this).attr('del-url');
$.post(delUrl, function() {
$(self).closest('tr').fadeOut('slow');
},'json');
});
This line:
$.post(delUrl, null, removeRow(),'json');
simply equivalent to this:
$.post(delUrl, null, undefined, 'json');
as removeRow() function does not return anything. Change that line to this:
$.post(delUrl, null, removeRow,'json'); //Without '()' after 'removeRow'
And your removeRow function will not work as you expected I guess. That function will hide all the rows, but you need to hide only that one, which 'delete-file' is clicked. So you need to pass reference of that row to removeRow function. Here is one way of doing that:
$('#delete-file').click(function() {
var delUrl = $(this).attr('del-url');
var $row = $(this).closest('tr');
$.post(delUrl, null, function(){
removeRow($row);
},'json')
});
function removeRow($row) { $row.fadeOut('slow'); }
I have a following script:
$(document).ready(function() {
$('#q1_6').on("click", function(event){
$('#q1-f').slideToggle(350);
});
$('#q1_7').on("click", function(event){
$('#q1-m').slideToggle(350);
});
$('#q1_15').on("click", function(event){
$('#q1-o').slideToggle(350);
});
$('#q2_6').on("click", function(event){
$('#q2-f').slideToggle(350);
});
$('#q2_7').on("click", function(event){
$('#q2-m').slideToggle(250);
});
$('#q2_15').on("click", function(event){
$('#q2-o').slideToggle(350);
});
$('#q3_13').on("click", function(event){
$('#q3-o').slideToggle(350);
});
});
Are these calls proper, or maybe I should somehow refactor the script to avoid duplication?
Edit:
I am creating a survey with about 20 questions displayed on one page. Answers are in checkboxes. Some answers have additional options (sub-answers), which should be shown when user clicks parental answer. Here is HTML markup for better understanding
<div>
<input type="checkbox" id="q1_5"/><label for="q1_5">Answer 5</label>
</div>
<div>
<input type="checkbox" id="q1_6"/><label for="q1_6">Answer 6</label>
</div>
<div id="q1-f">
<div>
<input type="checkbox" id="q1_6_1"/><label for="q1_6_1">Answer 6-1</label>
</div>
<div>
<input type="checkbox" id="q1_6_2"/><label for="q1_6_2">Answer 6-2</label>
</div>
<div>
<input type="checkbox" id="q1_6_3"/><label for="q1_6_3">Answer 6-3</label>
</div>
</div>
Current script works well, but I am wondering if I can avoid repeats of the same code snippets.
If you have access to the HTML and can influence the structure of the elements which when clicked initiate the toggle, then I'd add a class and data attribute:
<a href='#' id='q1_6' class='toggle' data-toggle-id='q1-f'>blah</a>
$(document).ready(function() {
$('a.toggle').on('click', function(){
var $el = $(this),
toggleID = '#' + $el.attr('data-toggle-id'),
toggleValue = 350;
$(toggleID).slideToggle(toggleValue);
});
});
given no idea on how IDs are correlated in your example.
function toggle(id, id2, value){
$(id).on("click", function(event){
$(id2).slideToggle(value);
});
}
toggle('#q1_15', '#q1-0', 350);
You could do :
$('[id^="q"]').on("click", function(e){
//get the id of the clicked button
var id = e.target.id;
switch(id){
//do all cases based on id
}
});
This could be done in an even cleaner way if there is some element to which we could delegate the event handling, but you didn't show us your markup. It would be something like
$('body').on("click",'[id^="q"]', function(e){
//get the id of the clicked button
var id = e.target.id;
switch(id){
//do all cases based on id
}
});
This second option use only one event handler (good) but it has to wait until the event is bubbled up the DOM (might be bad)
Why don't you create a javascript object where keys denote the id of element that accepts clicks and corresponding value is the id of element to show. Something like:
var clickHanders = {
"q1_6": "q1-f",
"q1_7": "q1-m"
// ...
};
Then all you need is:
$(document).ready(function() {
$("div[id^=q]").on("click", function(event) {
var srcId = this.id;
var dstId = clickHanders[srcId];
if (dstId) {
$("#" + dstId).slideToggle(350);
}
});
});
Edit
I now see that the slide duration can be different as well. You can still use the above approach:
var clickHanders = {
"q1_6": {
"elementId": "q1-f",
"duration": "350"
},
"q2_7": {
"elementId": "q2-m",
"duration": "250"
}
//...
};
$(document).ready(function() {
$("div[id^=q]").on("click", function(event) {
var srcId = this.id;
var dstData = clickHanders[srcId];
if (dstData) {
$("#" + dstData.elementId).slideToggle(dstData.duration);
}
});
});
This code looks longer than your original code but perhaps more sensible.
There are some things to ask when you refactor:
Are you having speed problems?
Are you having memory problems?
Does the code look ugly?
You should not be refactoring code just to refactor it and optimize something where it is not needed. If the code is not too slow and does not have a too high memory consumption (all in the eye of the beholder) and is readable, you have good code.
My idea would be to serialize the event trigger, event target and slideToggle amount, so you could iterate through some kind of Collection and bind it. But would that be worth it?
Well, each handler does the very same thing, so here's what you should do:
Refactor your HTML, for example:
<div id="q1-6" data-to="q1-f"></div>
for each object (you can use other HTML attributes). Then you can define handlers:
$('#q1-6,#q1-7,#someother_id,#watman,#foo').click(function() {
var id = $(this).attr('data-to');
$('#'+id).slideToggle(350);
});
There is no way to shorten your code, because there is nothing exactly the same, so your code is ok.