I'm trying to add a drop event in my backbone view but I can't get it right. Below is the code I'm using and you can also check out this jsfiddle.
var View = Backbone.View.extend({
events: {
'drop .dropzone': 'drop'
},
drop: function(e) {
e.preventDefault();
alert('Dropping');
},
initialize: function() {},
render: function() {}
});
$(function() {
var view = new View({
el: $('.dropzone');
});
});
if your view's el == .dropzone then you should do
events: {
'drop': 'drop'
},
if you specify just event type without selector it will listen to event on the view's root element - $('.dropzone') in this case.
The way you did it here would try to find .dropzone element among children of .dropzone element - and clearly it would fail to find this element at all
Related
I've got a problem with my Backbone.js app (Fiddle: http://jsfiddle.net/the_archer/bew7x010/3/). The app is list (ol element) with nested lists within. Here's my starting HTML:
<body>
<ol id="flowList"></ol>
</body>
<script type="text/template" id="item-template">
<%= content %>
</script>
When the list is empty. I add a new <li> element within it using Backbone.js and focus on the li.
When within the li, if I press the enter key, I want to insert a new li just after the li I pressed enter within.
If I press tab, I want to add a new sub ol element within the list element. I handle the keypress within the li elements like so:
handleKeyboardShortcuts: function(e){
if (e.keyCode == 13 && !e.shiftKey){
e.preventDefault();
this.el = $(e.target).parent();
Items.create({contnet: "New Item!"});
}
}
I have a listenTo on my collection, which on add, appends a li to the #flowList ol element using the addOne function:
addOne: function(todo) {
var view = new ItemView({model: todo});
$(this.el).append(view.render().el);
}
My problem is:
How do I pass the target element to add to, to the addOne function?
How do I pass an option to the addOne function, which instead of doing $(this.el).append does $(this.el).after ?
For some reason, I can't get my head around how to pass around those details. Here's the full Backbone.js code:
$(function() {
var Item = Backbone.Model.extend({
defaults: function() {
return {
content: "empty item..."
};
}
});
var ItemList = Backbone.Collection.extend({
model: Item,
localStorage: new Backbone.LocalStorage("todos-backbone"),
});
var Items = new ItemList;
var ItemView = Backbone.View.extend({
tagName: "li",
template: _.template($('#item-template').html()),
events: {
"click": "enableEdit",
"blur": "disableEdit",
},
initialize: function() {
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destroy', this.remove);
},
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
},
enableEdit: function(){
this.$el.attr("contenteditable","true").focus();
},
disableEdit: function(){
this.$el.attr("contenteditable","false");
}
});
var AppView = Backbone.View.extend({
el: $("#flowList"),
events: {
"keydown li": "handleKeyboardShortcuts"
},
initialize: function() {
this.listenTo(Items, 'add', this.addOne);
this.listenTo(Items, 'reset', this.addAll);
this.listenTo(Items, 'all', this.render);
Items.fetch();
if (Items.length === 0){
Items.create({content: "Sample Item!"});
}
},
render: function(e) {
console.log(e);
},
addOne: function(todo) {
var view = new ItemView({model: todo});
$(this.el).append(view.render().el);
},
addAll: function() {
Items.each(this.addOne, this);
},
handleKeyboardShortcuts: function(e){
if (e.keyCode == 13 && !e.shiftKey){
e.preventDefault();
this.el = $(e.target).parent();
Items.create({contnet: "New Item!"});
}
}
});
var App = new AppView;
});
Here's a link to the fiddle: http://jsfiddle.net/the_archer/bew7x010/3/
You could keep track of the selected item, enabling you to insert the new one after or within it.
In your existing enableEdit handler, trigger an 'item:select' event:
var ItemView = Backbone.View.extend({
enableEdit: function(){
this.trigger('item:select', this);
this.$el.attr("contenteditable","true").focus();
}
});
Then in your AppView, when you add a new item, attach a listener for the new item that updates the value of a 'selectedItem' property.
Then you can insert the new item into the dom based on the state of that property:
var AppView = Backbone.View.extend({
addOne: function(todo) {
var view = new ItemView({model: todo});
this.listenTo( view, 'item:select', this.handleItemSelect );
if(this.selectedItem) {
$(this.selectedItem.el).after(view.render().el);
}
else {
$(this.el).append(view.render().el);
}
},
handleItemSelect: function(item) {
console.log('item selected:', item);
this.selectedItem = item;
}
});
You should be able to do something similar for the tab key behaviour, by setting a flag when the tab key is pressed, before calling Items.create.
I have a jquery to add li to existing ul by clicking a button and remove one by one by clicking other button. Creation is working but deletion is not working. I used append and remove to add and delete li.
Here is my code:
$(document).ready(function(){
var ListView = Backbone.View.extend({
el: $('body'), // el attaches to existing element
events: {
'click button#add': 'addItem',
'click button#remove': 'removeItem'
},
initialize: function(){
_.bindAll(this, 'render', 'addItem','removeItem');
this.counter = 0; // total number of items added thus far
this.render();
},
render: function(){
$(this.el).append("<button id='add'>Add list item</button>");
$(this.el).append("<button id='remove'>Remove list item</button>");
$(this.el).append("<ul></ul>");
},
addItem: function(){
this.counter++;
$('ul', this.el).append("<li>hello world"+this.counter+"</li>");
},
removeItem: function(){
alert(this.counter);
$('li',this.el).remove("<li>hello world"+this.counter+"</li>");
}
});
var listView = new ListView();
});
You can either call remove() on an element to remove itself, or give it an valid selector to remove some other elements. So you can try either
$('li:contains("hello world' + this.counter+ '")', this.el).remove()
or
$('ul', this.el).remove('li:contains("hello world' + this.counter+ '")')
Here I'm using the :contains selector. In your scenario you can also use the :last-child selector which simplifies it as:
$('li:last-child', this.el).remove()
or
$('ul', this.el).remove('li:last-child')
Looks like you are trying to remove last element on click of remove, if that is the case. you can change remove item to
addItem: function(){
this.counter++;
$('ul', this.el).append("<li class='count-"+this.counter+"'>hello world"+this.counter+"</li>");
},
removeItem: function(){
$('li.counter-'+this.counter+,this.el).remove()
}
Your cant't remove the item by passing it's whole html to the remove method, you need to use a selector, so either an id, class, type or whatever.
removeItem: function(e){
$('ul',this.el).remove('li:nth-child('+counter+')');
}
This would remove one li from the ul depending on your counter. If you want to remove one special li you will need to change the setup, because how could the button know which li to remove on click.
You can make the li itself the remove button for example:
events: {
'click li': 'removeLi'
}
and as remove function:
removeLi: function(e){
$(e.target).remove();
}
Hope it helps.
Or, sticking to your counter thingy:
removeItem: function(){
alert(this.counter);
this.$('li').eq(this.counter-1).remove();
this.counter--;
}
jQuery.eq()
Your remove function is buggy.
You can use the 'nth-child(nth)' css selector for removing the item...
removeItem: function(){
$('ul ' + 'li:nth-child('+this.counter+')',this.el).remove()
this.counter--;
}
I currently have a bootstrap popover holding a button. The popover shows only when the mouse is over a table's tr.
What I want to do is to be able to access the elements for that row, is this possible.
Popover code:
$('.popup').popover(
{
placement: 'top',
trigger: 'manual',
delay: { show: 350, hide: 100 },
html: true,
content: $('#shortcuts').html(),
title: "Quick Tasks"
}
).parent().delegate('#quickDeleteBtn', 'click', function() {
alert($(this).closest('tr').children('td').text()); // ???
});
var timer,
popover_parent;
function hidePopover(elem) {
$(elem).popover('hide');
}
$('.popup').hover(
function() {
var self = this;
clearTimeout(timer);
$('.popover').hide(); //Hide any open popovers on other elements.
popover_parent = self
//$('.popup').attr("data-content","WOOHOOOO!");
$(self).popover('show');
},
function() {
var self = this;
timer = setTimeout(function(){hidePopover(self)},250);
});
$(document).on({
mouseenter: function() {
clearTimeout(timer);
},
mouseleave: function() {
var self = this;
timer = setTimeout(function(){hidePopover(popover_parent)},250);
}
}, '.popover');
HTML:
<div class="hide" id="shortcuts">
Delete
</div>
javascript that implements popover on row:
rows += '<tr class="popup datarow" rel="popover">';
Does anyone know what I'm doing wrong here and how I am supposed to access the child elements of the tr I'm hovering over?
JSFiddle: http://jsfiddle.net/C5BjY/8/
For some reason I couldn't get closest() to work as it should. Using parent().parent() to get to the containing .popover divider, then using prev() to get the previous tr element seems to do the trick however.
Just change:
alert($(this).closest('tr').children('td').text());
To:
alert($(this).parent().parent().prev('tr').children('td').text());
JSFiddle example.
As a side note, as your Fiddle uses jQuery 1.10.1 you should change delegate() to on():
on('click', '#quickDeleteBtn', function(index) { ... });
Here I have fixed it.
You just have to pass the container option in which the popover element is added for the popover
$('.popup').each(function (index) {
console.log(index + ": " + $(this).text());
$(this).popover({
placement: 'top',
trigger: 'manual',
delay: {
show: 350,
hide: 100
},
html: true,
content: $('#shortcuts').html(),
title: "Quick Tasks",
container: '#' + this.id
});
});
In your button click alert, $(this) refers to the button itself. In the DOM hierarchy, the popover html is nowhere near your hovered tr.
Add a handler to the list item to store itself in a global variable and access that from the click event. See the forked fiddle here.
First we declare a global (at the very top):
var hovered;
Then we add a mouseover handler to the list item. Note that using 'on' means every newly generated list item will also receive this handler:
$('body').on('mouseover', '.popup', function() {
hovered = $(this);
});
Then we can alert the needed data from within the button click event:
alert(hovered.text());
See here JS Fiddle
by removing the delegate and using the id to find the button and attaching it to a click handler by making the popover makes it easier to track it
$(self).popover('show');
$('#quickDeleteBtn').click(function(){
alert($(self).text());
});
also note
$('#shortcuts').remove();
because you were using the button in the popover with the same ID in the #shortcuts we couldn't select it first, now we remove it we can
You already have the correct element in your code. Just reuse the popover_parent variable and you are all set :) FIDDLE
alert($(popover_parent).text());
Or you could do something around like this :
$('.popup').hover(
function () {
var self = this;
clearTimeout(timer);
$('.popover').hide(); //Hide any open popovers on other elements.
$('#quickDeleteBtn').data('target', '');
popover_parent = self;
//$('.popup').attr("data-content","WOOHOOOO!");
$('#quickDeleteBtn').data('target', $(self));
$(self).popover('show');
},
function () {
var self = this;
timer = setTimeout(function () {
$('#quickDeleteBtn').data('target', '');
hidePopover(self)
}, 250);
});
$(document).on({
mouseenter: function () {
clearTimeout(timer);
},
mouseleave: function () {
var self = this;
timer = setTimeout(function () {
$('#quickDeleteBtn').data('target', '');
hidePopover(popover_parent)
}, 250);
}
}, '.popover');
I just store the element clicked in your #quickDeleteBtn then use the link.
FIDDLE HERE
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Prevent execution of parent event handler
I need to attach functions to onclick events of hierarchical divs.
I have this HTML
<div onclick="event1()" class="wrapper">
main contents
<div onclick="event2()"class="inner">
inner contents
</div>
</div>
now when i click on inner div event1() is being called, and event2() is not being called because I think my jquery plugin blocks it.
Edited ::
actually my plugin blocks the child node events so event2() is never being called how can i stop that ?
I am using jquery full callender plugin : http://arshaw.com/fullcalendar/
and below is my configuration function which is being called on onready.
function calenderEvents(events, account_id) {
//Dynamically Set options as account type wise
var selectable_opt = '';
if (account_id == 'default') {
selectable_opt = true;
} else {
selectable_opt = false;
}
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
var calendar = $('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay'
},
selectable: selectable_opt,
selectHelper: true,
eventDrop: function(event, dayDelta, minuteDelta, allDay, revertFunc) {
AfterMove(event);
},
select: function(start, end, allDay) {
var title = prompt('Event Title:');
if (title) {
var details = {
title: title,
start: start,
end: end,
allDay: allDay
};
$.post(SITE_URL + '/calendar/add-event', {
details: details
}, function() {
});
calendar.fullCalendar('renderEvent', {
title: title,
start: start,
end: end,
allDay: allDay,
}, true // make the event "stick"
);
}
calendar.fullCalendar('unselect');
},
/*eventMouseover: function() {
$('.fc-event-delete').css('display','block');
},
eventMouseout: function() {
$('.fc-event-delete').css('display','none');
},*/
editable: true,
events: events,
});
//}).limitEvents(2);
}
You can add the event handler to the container element and supply a selector so only events triggered by elements that match that selector will invoke the handler. Because the handler is being attached to the containing element, child elements that are added to the DOM later will still invoke the handler, if they match the selector.
http://api.jquery.com/on/
This code will create an event handler that will be triggered on new elements that are added to the div#wrapper element. The #adder click handler will add new elements to the wrapper.
HTML
<div id="adder">click to add elements</div>
<div class="wrapper">
contents:
<div class="inner">0</div>
</div>
JS
var $inner = $('.inner').first(),
$wrapper = $('.wrapper'),
count = 0;
$wrapper.on('click', '.inner', function(e) {
alert('click from ' + $(this).text());
});
$('#adder').on('click', function() {
$wrapper.append($inner.clone().text(++count));
});
The main thing is the use of the .inner selector when the click event handler is added to $wrapper.
Shown in this jsFiddle.
You need to stop the event being propagated to the parent.
Use event.stopPropagation();
$(".inner").click(function(event){
//do something
event.stopPropagation();
});
This effect is call event propagation.
Inner div click handler has to be just like this to prevent propagation:
var event2 = function(event) {
event = event || window.event;
if (event.stopPropagation) {
// for adequate browsers
event.stopPropagation()
} else {
// for IE
event.cancelBubble = true
}
}
demo - http://jsfiddle.net/Qw92P/
Just use one click event on the wrapper but make it "live". Detect if the click was actually on the child using targetElement (or is it srcElement--you can look up this part).
So here is my view:
$(function() {
var ImageManipulation = Backbone.View.extend({
el: $('body'),
tagName: "img",
events: {
'mouseover img': 'fullsize',
'click img#current': 'shrink'
},
initialize: function() {
_.bindAll(this, 'render', 'fullsize', 'shrink');
//var message = this.fullsize;
//message.bind("test", this.fullsize);
},
render: function() {
},
fullsize: function() {
console.log("in fullsize function");
console.log(this.el);
$('.drop-shadow').click(function() {
console.log(this.id);
if (this.id != 'current') {
$('.individual').fadeIn();
$(this).css('position', 'absolute');
$(this).css('z-index', '999');
$(this).animate({
top: '10px',
height: '432px',
}, 500, function() {
this.id = "current";
console.log("animation complete");
return true;
});
};
});
},
shrink: function() {
$('.individual').fadeOut();
$('#current').animate({
height: '150px',
}, 500, function() {
this.id = "";
$(this).css('position', 'relative');
$(this).css('z-index', '1');
console.log("animation complete");
return true;
});
}
});
var startImages = new ImageManipulation();
});
What I don't understand is how to change the el to make 'this' take over the click function I have in full-size. I would much rather have the click jQuery function removed and have the mouseover function be another click, but I cant seem to figure out how to assign 'this' to the particular image that is being clicked. I hope my question makes sense.
Backbone's event handler assumes that you want to know about the object (both its code, and its DOM representation, the View.el object) for every event, and that the event is intended to change some aspect of the view and/or model. The actual target of the click is something you're assumed to know, or assumed to be able to derive.
Derivation is rather simple:
fullsize: function(ev) {
target = $(ev.currentTarget);
And replace all your this. references within your call to target.. this. will continue to refer to the View instance. In your inner function, the anonymous one assigned to .drop-shadow, this. will refer to the object that was just clicked on. If you want access to the surrounding context, use the closure forwarding idiom:
fullsize: function(ev) {
var target = ev.currentTarget;
var self = this;
$('.drop-shadow').click(function(inner_ev) {
console.log(this.id); // the same as inner_ev.currentTarget
console.log(self.cid); // the containing view's CID