Why do I lose draggable after drag? - jquery-ui

I just migrated to backbone and have a strange behaviour.
I attach draggable to an element which is created by a script, thus not directly available in DOM.
EDIT:
The element that is created is .nav, $("#viewer") as container is already in the DOM.
In plain jQuery i used .on and mousemove event for this and it worked.
With backbone I use the same in the initialize method:
initialize: function(options) {
this.viewer = $("#viewer");
this.viewer.on("mousemove", '.nav', function() {
$(this).draggable();
});
This seems to work, but only one time.
After dragging the element one time, I can't drag it anymore.
Are there conflicts with the events? Am I missing something?

You have to refer to $('#viewer') after you've called render(). initialize is called before render, and so the DOM element doesn't exist.
Also, use this.$('#viewer'), and it will grab the element (after render) even if it hasn't been appended to your page's DOM.
myView = new ExampleView({ model: myModel });
$(body).append(myView.render().el);
myView.onRender();
// -------------
// Now on your view:
onRender: function() {
this.viewer = this.$('#viewer');
this.viewer.on("mousemove", '.nav', function() {
$(this).draggable();
});
},
UPDATE
You can also, to make such things simpler, customize Backbone to automatically call the onRender() function after rendering, by triggering an event or something.
Marionette.js (a Backbone.js extension) has this built in and I use it all the time.

The solution finally was pretty easy:
make sure you dont use outdated versions of backbone.js and underscore!!
After i updated the versions to latest I made it work with:
render: function() {
this.viewer.on("mouseover", '.nav', function() {
if (!$(this).data("init")) {
$(this).data("init", true);
$(this).draggable();
}
});
Probably still not very elegant but i couldnt made the suggested onRender method from dc2 work.

Related

Jquery mobile How to tap the screen to no avail

I tested on the Apple device, and when I click on the screen when there is no effect. This is my code. Click on the events of this writing there are questions?
<script>
$(function() {
$('#test').tap(function() {
$('#menuNum').text('1');
})
})
</script>
You need to change few things.
Do not use $(function() { or classic document ready to check for a correct state, they can cause problems with jQuery Mobile. Instead use jQuery Mobile alternative called page events.
Then don't bind tap event like that, use proper modern way of doing that. In your case element must be loaded into the DOM for that kind of binding to work. And because of $(function() { sometimes it can happen that element is still loading when binding is executed. So use it like this:
$(document).on('tap','#test',function() {
$('#menuNum').text('1');
});
This method don't care if element exist or not, it will even work if element is loaded into the DOM after binding process.
Working example: http://jsfiddle.net/Gajotres/SQ7DF/
In the end you want something like this:
$(document).on('pagebeforeshow', '#index', function(){
$(document).on('tap','#test',function() {
alert('Tap');
});
});

JQM - Inject dynamic content at load time only

I'm trying to dynamically populate a select tag at load time (latest jQM version) using a custom template filling function.
If the fn is called in the "pagebeforechange" event, the select tag is properly initialized. Since this event is called on every page transition, I thought of moving the fn to the 'pageinit' event. This does not work, presumably because the DOM is not yet fully available. How can I coerce jQM to inject content in a page only once? Currently, I am using a kludge. There surely must be a smarter way. Thanks for any suggestions.
$(document).bind('pageinit', function () {
InitSelTagTest("#selActTag", "tplTag"); // Does not work.
});
$(document).bind("pagebeforechange", function (e, data) {
if ($("#selActTag").children().size() === 0) {
InitSelTagTest("#selActTag", "tplTag"); // Kludge, but it works
}
});
function InitSelTagTest(el,tpl) { // Append all tags to element el
var lstAllTags = JSON.parse($("#hidTag").val()); // Create tag array
// Retrieve html content from template.
var cbeg = "//<![" + "CDATA[", cend = "//]" + "]>";
var rslt = tmpl(tpl, { ddd: lstAllTags }).replace(cbeg, ").replace(cend,");
$(el).html(rslt).trigger("create"); // Add to DOM.
}
EDIT
In response to Shenaniganz' comment, it seems that the "pagebeforecreate" event could do the trick ie.
$("#pgAct").live("pagebeforecreate", function () {
// Populate tag select. Works. Traversed only once.
InitSelTag("#selActTag", "tplTag");
});
I'm not sure I fully understand your question but I'll throw a few things out there and you let me know if I can extend further.
To make something trigger only once on page load you can try to implement a regular JQuery $(document).ready(function(){}) aka $(function(){}) for the exact reason why JQuery Mobile users are told not to use it. It triggers only once on DOM load. Further pages don't trigger it because they're being switched via Ajax.
Other than that, on regular dynamic content loading you take a look at the following example I put together for someone else earlier:
http://jsbin.com/ozejif/1/edit

Jquery calls not working in $viewContentLoaded of Angular

Unable to call jquery functions in $viewContentLoaded event of Angular controller, here is the code for the same.
$scope.$on('$viewContentLoaded', function() {
jQuery.growlUI('Growl Notification', 'Saved Succesfully');
jQuery('#category').tree()
});
Is any configuration required here?? I tried even noConflict(); var $jq = jQuery.noConflict();
Does it require any other configuration?
Thanks,
Abdul
First thing first, don't do DOM manipulation from controller. Instead do it from directives.
You can do same thing in directive link method. You can access the element on which directive is applied.
Make sure you load jquery before angularjs scripts, then grawlUI, three, angularJS and finally your application script. Below is directive sample
var app = angular.module("someModule", []);
app.directive("myDirective", function () {
return function (scope, element, attrs) {
$.growlUI('Growl Notification', 'Saved Succesfully');
element.tree();
};
});
angularjs has built in jQuery lite.
if you load full jquery after angular, since jQuery is already defined, the full jquery script will skip execution.
==Update after your comment==
I reviewed again your question after comment and realised that content which is loaded trough ajax is appended to some div in your angular view. Then you want to apply element.tree() jquery plugin to that content. Unfortunately example above will not work since it is fired on linking which happened before your content from ajax response is appended to element with directive I showed to you. But don't worry, there is a way :) tho it is quick and dirty but it is just for demo.
Let's say this is your controller
function ContentCtrl($scope, $http){
$scope.trees=[];
$scope.submitSomethingToServer=function(something){
$http.post("/article/1.html", something)
.success(function(response,status){
// don't forget to set correct order of jquery, angular javascript lib load
$.growlUI('Growl Notification', 'Saved Succesfully');
$scope.trees.push(response); // append response, I hope it is HTML
});
}
}
Now, directive which is in controller scope (it uses same scope as controller)
var app = angular.module("someModule", []);
app.directive("myDirective", function () {
return function (scope, element, attrs) {
scope.$watch("trees", function(){
var newParagraph=$("<p>" + scope.trees[scope.trees.length-1] + "</p>" ); // I hope this is ul>li>ul>li...or what ever you want to make as tree
element.append(newParagraph);
newParagraph.tree(); //it will apply tree plugin after content is appended to DOM in view
});
};
});
The second approach would be to $broadcast or $emit event from controller (depends where directive is, out or in scope of controller) after your ajax completes and you get content from server. Then directive should be subscribed to this event and handle it by receiving passed data (data=content as string) and do the rest as I showed you above.
The thing is, threat that content from ajax as data all the way it comes to directive, then inject it to element in which you want to render it and apply tree plugin to that content.

jQuery UI in Backbone View adds elements, but doesn't respond to events

I'm building an app in which I'm using Django on the backend and jQuery UI/Backbone to build the front. I'm pulling a Django-generated form into a page with jQuery.get() inside of a Backbone View. That part works fine, but now I want to add some jQuery UI stuff to the form (e.g. a datepicker, some buttons that open dialogs, etc). So, here's the relevant code:
var InstructionForm = Backbone.View.extend({
render: function() {
var that = this;
$.get(
'/tlstats/instruction/new/',
function(data) {
var elements = $(data);
$('#id_date', elements).datepicker();
that.$el.html(elements.html());
}
};
return this;
}
});
The path /tlstats/instruction/new/ returns an HTML fragment with the form Django has generated. What's happening is that input#id_date is getting the hasDatePicker class added and the datepicker div is appended to my <body> element (both as expected), but when I click on input#id_date, nothing happens. No datepicker widget appears, no errors in the console. Why might this be happening?
Also, somewhat off-topic, but in trying to figure this problem out on my own, I've come across several code examples where people are doing stuff like:
$(function() {
$('#dialog').dialog(...);
...
});
Then later:
var MyView = Backbone.View.extend({
initialize(): function() {
this.el = $('#dialog');
}
});
Isn't this defeating the purpose of Backbone, having all that jQuery UI code completely outside any Backbone structure? Or do I misunderstand the role of Backbone?
Thanks.
I think your problem is right here:
$('#id_date', elements).datepicker();
that.$el.html(elements.html());
First you bind the datepicker with .datepicker() and then you throw it all away by converting your elements to an HTML string:
that.$el.html(elements.html());
and you put that string into $el. When you say e.html(), you're taking a wrapped DOM object with event bindings and everything else and turning into a simple piece of HTML in a string, that process throws away everything (such as event bindings) that isn't simple HTML.
Either give .html() the jQuery object itself:
$('#id_date', elements).datepicker();
that.$el.html(elements);
or bind the datepicker after adding the HTML:
that.$el.html(elements);
that.$('#id_date').datepicker();

Bind jQuery UI autocomplete using .live()

I've searched everywhere, but I can't seem to find any help...
I have some textboxes that are created dynamically via JS, so I need to bind all of their classes to an autocomplete. As a result, I need to use the new .live() option.
As an example, to bind all items with a class of .foo now and future created:
$('.foo').live('click', function(){
alert('clicked');
});
It takes (and behaves) the same as .bind(). However, I want to bind an autocomplete...
This doesn't work:
$('.foo').live('autocomplete', function(event, ui){
source: 'url.php' // (surpressed other arguments)
});
How can I use .live() to bind autocomplete?
UPDATE
Figured it out with Framer:
$(function(){
$('.search').live('keyup.autocomplete', function(){
$(this).autocomplete({
source : 'url.php'
});
});
});
jQuery UI autocomplete function automatically adds the class "ui-autocomplete-input" to the element. I'd recommend live binding the element on focus without the "ui-autocomplete-input"
class to prevent re-binding on every keydown event within that element.
$(".foo:not(.ui-autocomplete-input)").live("focus", function (event) {
$(this).autocomplete(options);
});
Edit
My answer is now out of date since jQuery 1.7, see Nathan Strutz's comment for use with the new .on() syntax.
If you are using the jquery.ui.autocomplete.js try this instead
.bind("keydown.autocomplete") or .live("keydown.autocomplete")
if not, use the jquery.ui.autocomplete.js and see if it'll work
If that doesn't apply, I don't know how to help you bro
Just to add, you can use the .livequery plugin for this:
$('.foo').livequery(function() {
// This will fire for each matched element.
// It will also fire for any new elements added to the DOM.
$(this).autocomplete(options);
});
To get autocomplete working when loaded dynamically for the on() event used in jQuery > 1.7, using the syntax Nathan Strutz provides in his comment:
$(document).on('focus', '.my-field:not(.ui-autocomplete-input)', function (e) {
$(this).autocomplete(options)
});
where .my-field is a selector for your autocomplete input element.
.live() does not work with focus.
also keyup.autocmplete does not make any sense.
Instead the thing I have tried and working is this
$(document).ready(function(){
$('.search').live('keyup' , function()
{
$(this).autocomplete({ source : 'url.php' });
});
})
This works perfectly fine.
You can't. .live() only supports actual JavaScript events, not any custom event. This is a fundamental limitation of how .live() works.
You can try using this:
$('.foo').live('focus.autocomplete', function() {
$(this).autocomplete({...});
});
After reading and testing everyone else's answers I have updated it for the current version of JQuery and made a few tweaks.
The problem with using keydown as the event that calls .autocomplete() is that it fails to autocomplete for that first letter typed. Using focus is the better choice.
Another thing I have noticed is that all of the given solutions result in .autocomplete() being called multiple times. If you are adding an element dynamically to the page that will not be removed again, the event should only be fired once. Even if the item is to be removed and added again, the event should be removed and then added back each time the element is removed or added so that focusing on the field again will not unnecessarily call .autocomplete() every time.
My final code is as follows:
$(document).on('focus.autocomplete', '#myAutocomplete', function(e){
$(this).autocomplete(autocompleteOptions);
$(document).off('focus.autocomplete', '#myAutocomplete');
});
autocomplete is not an event rather a function that enables autocomplete functionality for a textbox.
So if you can modify the js that creates the textboxes dynamically to wrap the textbox element in as a jquery object and call autocomplete on that object.
I just noticed you edited your post with this answer. It was obvious to me so I'm posting it below for others. Thank you.
$(function()
{
$('.search').live('keyup.autocomplete', function()
{
$(this).autocomplete({ source : 'url.php' });
});
});
This works for me:
$(function()
{
$('.item_product').live('focus.autocomplete', function()
{
$(this).autocomplete("/source.php/", {
width: 550,
matchContains: true,
mustMatch: false,
selectFirst: false,
});
});
});
You can just put the autocomplete inside input live event, like this:
$('#input-element').live('input', function(){
$("#input-element").autocomplete(options);
});

Resources