Cursor position in textbox inside jquery autocomplete callback? - javascript

Autocomplete using css class:-
$(cssClass).autocomplete({
minLength: 0,
focus: function() {
return false;
},
select: function( event, ui ) {
var cursorPos = $(this).prop('selectionStart');
console.log(cursorPos);// showing the proper cursor value
return false;
},
source: function( request, response ){
var cursorPos = $(this).prop('selectionStart');
console.log(cursorPos);// undefined ...?
}
});
When I am trying to get the cursor position using css class using jquery autocomplete, in one closure select I am able to successfully retrieve the value of current position but when the same is done in source closure the value is undefined.
Can someone please explain me why is that? and how to get the value?

To get the cursor position inside the source, you can try search event method. This method will be called once you type/change content in the input/text area and then only source method will be called. So, what you can do is, have a global variable cursorPos and in the search method, do the assignment as cursorPos = $(this).prop('selectionStart');
Inside the source method, you can get the global variable cursorPos value.
Your code can be modified as,
var cursorPos; // global var
$(cssClass).autocomplete({
minLength: 0,
focus: function() {
return false;
},
select: function( event, ui ) {
var cursorPos = $(this).prop('selectionStart');
console.log(cursorPos);
return false;
},
source: function( request, response ){
console.log(cursorPos); // refers the global var and returns the cursor pos
// add your logic
},
search: function( event, ui ){
cursorPos = $(this).prop('selectionStart');
}
});
By this way, you can get the cursor position inside the source method :)

Related

How to get ID of link that opened Fancybox3 within onComplete?

I have seen what looks like it could be the solution to this issue in other questions on stackoverflow, but NONE of them have worked.
I am trying to get the innertext of the link that was clicked to open Fancybox:
$("a.turnDateLink").each(function() {
var that = $(this);
$( "a.turnDateLink" ).fancybox({
'type': 'modal',
'onComplete': function() {
var currentday = $(that).text();
console.log(currentday);
},
'afterClose': clearCurrentDay,
'fullScreen' : false
});
});
It only returns the innerText of the last a.turnDateLink. EVERY TIME! ugh.
Keep in mind that I would like to use the fancybox grouping with this.
See the Fancybox documentation to understand grouping: http://fancyapps.com/fancybox/3/docs/#usage
If you have a group of items, you can use the same attribute
data-fancybox value for each of them to create a gallery. Each group
should have a unique value:
Check documentation about events - http://fancyapps.com/fancybox/3/docs/#events
The first examaple contains useful tips, including how to find clicked element:
onComplete: function( instance, slide ) {
// Tip: Each event passes useful information within the event object:
// Object containing references to interface elements
// (background, buttons, caption, etc)
// console.info( instance.$refs );
// Current slide options
// console.info( slide.opts );
// Clicked element
// console.info( slide.opts.$orig );
// Reference to DOM element of the slide
// console.info( slide.$slide );
}
Try like this.Change $( "a.turnDateLink" ) to that.
$("a.turnDateLink").each(function() {
var that = $(this);
that.fancybox({
'type': 'modal',
'onComplete': function() {
$("#currentday").html('');
var currentday = that.text();
console.log(currentday);
},
'afterClose': clearCurrentDay,
'fullScreen' : false
});
});

How to make a code reusable in jQuery?

In my WordPress' projects, I'm using the following code again and again for many of my fields where I'm using a button to initiate the WordPress media uploader and on selection of the file I'm sending its path/url to a text field.
var project_field_image_uploader;
$('#button-input').click( function(e) {
e.preventDefault();
//if the uploader object has already been created, reopen the dialog
if( project_field_image_uploader ) {
project_field_image_uploader.open();
return;
}
//extend the wp.media object
project_field_image_uploader = wp.media.frames.file_frame = wp.media( {
title:"Choose an image",
button:{
text: "Insert"
},
multiple: false
} );
//when a file is selected, grab the URL and set it as the text field's value
project_field_image_uploader.on( 'select', function() {
attachment = project_field_image_uploader.state().get('selection').first().toJSON();
$('#text-field').val(attachment.url);
});
//Open the uploader dialog
project_field_image_uploader.open();
});
For each of the field I need to edit the following things:
First variable - project_field_image_uploader (not necessarily it should be meaningful, it is only for creating different instances, so in a reusable way, it can be anything, but not conflicting)
Button's ID - $('#button-input')
Text field's ID - $('#text-field')
Media Library Modal's head - title:"Choose an image",
Media Library's Media Insertion button's text - text: "Insert"
Is there a way I can make this code reusable, so that I can be with DRY ideology? A jQuery function may do the job for me, but I cannot sort things out, how can I sort this thing.
<script>
$(function(){
$('#button-input').click(function(e){
var text_field = $('#text-field');
....................
var mytext = 'my text';
myfunc(e,project_field_image_uploader,text_field,mytitle,mytext);
});
//reuse with any other button click with different parameters
});
function myfunc(e,project_field_image_uploader,text_field,mytitle,mytext){
e.preventDefault();
//if the uploader object has already been created, reopen the dialog
if( project_field_image_uploader ) {
project_field_image_uploader.open();
return;
}
//extend the wp.media object
project_field_image_uploader = wp.media.frames.file_frame = wp.media( {
title:mytitle,
button:{
text: mytext
},
multiple: false
} );
//when a file is selected, grab the URL and set it as the text field's value
project_field_image_uploader.on( 'select', function() {
attachment = project_field_image_uploader.state().get('selection').first().toJSON();
text_field.val(attachment.url);
});
//Open the uploader dialog
project_field_image_uploader.open();
}
</script>
Thanks to #alamnaryab for his answer that directed me to the right way (+1 for that). But passing a variable as a function parameter was problematic. It produces an error:
project_field_image_uploader is not defined
I figured out things that, a variable need not to pass as a function parameter to be unique, because a variable inside a function is a local variable. So I simply called the variable inside the function and reused the function multiple times. I'm here posting the working example code.
And declaring multiple variables, I used comma with a single var declaration. There's no need to repeat things. Thanks again to Mr. Alam Naryab.
<script>
$(function(){
$('#button-input').click(function(e){
var text_field = $('#text-field'),
media_lib_head = 'Choose an image',
btn_text = 'Insert';
//using the function where necessary
project_reusable_repeating_func( e, text_field, media_lib_head, btn_text );
});
});
/**
* Reusable function
* #author alamnaryab
* #link http://stackoverflow.com/a/32035149/1743124
*/
function project_reusable_repeating_func( e, text_field, media_lib_head, btn_text ){
//a variable that need not to be unique, because it's local
var project_field_image_uploader;
e.preventDefault();
//if the uploader object has already been created, reopen the dialog
if( project_field_image_uploader ) {
project_field_image_uploader.open();
return;
}
//extend the wp.media object
project_field_image_uploader = wp.media.frames.file_frame = wp.media( {
title: media_lib_head,
button:{
text: btn_text
},
multiple: false
} );
//when a file is selected, grab the URL and set it as the text field's value
project_field_image_uploader.on( 'select', function() {
attachment = project_field_image_uploader.state().get('selection').first().toJSON();
text_field.val(attachment.url);
});
//Open the uploader dialog
project_field_image_uploader.open();
}
</script>

Use Methods from Dropped Object in JQuery

I would like to use a method of an dragged and dropped Object.
function Controller(){
$( "#container" ).droppable({
drop: function( event, ui ) {
var draggable = ui.draggable;
alert( "Dropped:" + draggable.attr('id'));
draggable.myMethod();
}
});
Could you explain why this doesnt work?
The alert shows that the right Object ist dropped,
but i cant use the method.
The Object looks like this:
function Icon(name) {
var name = name;
this.myMethod = function() {
alert("test")};
this.getInfo = function() {
var dataname = this.getName();
//BErtram
$("#" + dataname).draggable({ revert: "valid" });
//Bertram Ende
}
}
Edit:
If I invoke the Method this way:
drop: function( event, ui ) {
var draggable = ui.draggable;
alert(...);
var ic = new Icon("asdsa");
ic.myMethod();
},
it works, but I want to use the Method on the dragged Object, do I have to do some sort of typecasting?
Edit: Workaround
Finally i used a workaround, using a bool to check if there was a succesfull drop and invoking the method in draggable.stop if the boolean ist true. The state of the boolean is set to true in the droppable on a succesfull drop and set to false in draggable.start.
The problem is that myMethod is a function on the Icon object you create, however the ui.draggable is not an instance of Icon - so it has no myMethod method.
The ui.draggable value is a DOM element contained inside a jQuery object. You need a mechanism to map from the element back to the Icon that's responsible for it.
The simplest method is to use .data():
function Icon(name) {
$('#' + name).data('self', this);
...
}
and then retrieve that object in the event handler:
drop: function(event, ui) {
var draggable = ui.draggable;
var icon = draggable.data('self');
icon.myMethod();
}

Best way to silently bind window resize event to jQuery plugin without keeping a reference to the targeted element

I'm looking for best-practice advice.
I'm writing a small jQuery plugin to manage horizontal scroll on elements.
I need all the dom elements targeted by that plugin to update on window resize.
Fact is, my website is a full ajax 'app' so when I remove DOM elements, I need them gone so memory doesn't leak.
But I can't find a way to bind the resize event without keeping a reference to the DOM node.
EDIT :
Actually I need the resize handler to get the plugin-targeted elements at 'call' time, coz I don't want to keep any reference to those elements in memory, because I might call .html('') on a parent of theirs...
I did not paste all my code, just an empty shell. I already have a destroy method that unbinds handlers. But I'm generating, removing and appending html nodes dynamically and I the the elements targeted by the plugin to remove silently.
Kevin B stated I could override jQuery .remove method to deal with the handlers, but would have to load jQuery UI for it to work. I don't want that either..
Here is what I tried (attempts commented):
(function($) {
// SOLUTION 2 (see below too)
// Not good either coz elements are not removed until resize is triggered
/*
var hScrolls = $([]);
$(window).bind('resize.hScroll',function(){
if(!hScrolls.length) return;
hScrolls.each(function(){
if($(this).data('hScroll')) $(this).hScroll('updateDimensions');
else hScrolls = hScrolls.not($(this));
});
});
*/
// END SOLUTION 2
// SOLUTION 3 (not implemented but I think I'm on the right path)
$(window).bind('resize.hScroll',function(){
// need to get hScroll'ed elements via selector...
$('[data-hScroll]').hScroll('updateDimensions');
// I don't know how....
});
// END SOLUTION 3
var methods = {
init : function(options) {
var settings = $.extend( {
defaults: true
}, options);
return this.each(function() {
var $this = $(this),
data = $this.data('hScroll');
if (!data) {
$this.data('hScroll', {
target: $this
});
// SOLUTION 1
// This is not good: it keeps a reference to $this when I remove it...
/*
$(window).bind('resize.hScroll', function(){
$this.hScroll('updateDimensions');
});
*/
// END SOLUTION 1
$this.hScroll('updateDimensions');
// SOLUTION 2 (see above too)
hScrolls = hScrolls.add(this);
}
});
},
updateDimensions: function(){
var hScroll = this.data('hScroll');
// do stuff with hScroll.target
}
}
$.fn.hScroll = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if ( typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.hScroll');
}
};
})(jQuery);​
Thanks all in advance!
jQuery calls cleanData any time you do something that removes or replaces elements (yes, even if you use parent.html("") ). You can take advantage of that by extending it and having it trigger an event on the target elements.
// This is taken from https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js 10/17/2012
if (!$.widget) { // prevent duplicating if jQuery ui widget is already included
var _cleanData = $.cleanData;
$.cleanData = function( elems ) {
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
try {
$( elem ).triggerHandler( "remove" );
// http://bugs.jquery.com/ticket/8235
} catch( e ) {}
}
_cleanData( elems );
};
}
Now you can bind to the remove event when setting up your plugin and have it run your destroy method.
$(elem).bind("remove",methods.destroy)
You might use a class name and forward the resize event:
$.fn.hScroll = function(method) {
this
.addClass('hScroll')
.data('method', arguments)
};
var methods['alert_text'] = function(config){
alert( config + " " + $(this).text() );
}
$(window).bind('resize.hScroll',function(){
$(".hScroll").each(function(){
var method_config = $(this).data('method');
var method = method_config.shift();
// Forward the resize event with all resize event arguments:
methods[method].apply(this, method_config);
})
})
// Register a resize event for all a.test elements:
$("a.test").hScroll('alert_text', "hey");
// Would alert "hey you" for <a class="test">you</a> on every resize
Update
If you change the dom and want to keep the selector you might try this one:
var elements = [];
$.fn.hScroll = function(method) {
elements.push({'selector' : this.selector, 'arguments' : arguments });
};
var methods['alert_text'] = function(config){
alert( config + " " + $(this).text() );
}
$(window).bind('resize.hScroll',function(){
$.each(elements,function(i, element){
$(element.selector).each(function(){
var method_config = element.arguments;
var method = method_config.shift();
// Forward the resize event with all resize event arguments:
methods[method].apply(this, method_config);
})
})
})
// Register a resize event for all a.test elements:
$("a.test").hScroll('alert_text', "hey");
$(document.body).html("<a class='test'>you</a>");
// Would alert "hey you" for every window resize
You should have the scroll event bound in the extension. Also, you will want to add a "destroy" method to your extension as well. Before you remove the element from the DOM, you will want to call this method. Inside the detroy method is where you will want to unbind the resize event.
One important thing in making this work is that you have a reference to each handler method that is bound to the resize event. Alternatively, you can unbind All resize events upon the removal on an element and then rebind the scroll event to the remaining elements that require it.

jQuery Sortable - cancel and revert not working as expected

Problem (jsFiddle demo of the problem)
I'm having some trouble with the revert setting when used in conjunction with the cancel method in the jQuery sortable. The cancel method, as documented in the jQuery Sortable documentation states:
Cancels a change in the current sortable and reverts it back to how it
was before the current sort started. Useful in the stop and receive
callback functions.
This works fine in both the stop and receive callbacks, however if I add a revert duration to the sortable connected list, it starts to act funny (see jsFiddle here).
Ideally, upon cancelling, the revert could simply not happen, or alternatively in a more ideal world, it would gracefully revert to it's original location. Any ideas how I can get the revert and cancel to play nice?
Expected
Drag from left list to right list
Drop item
Item animates to original location - or - immediately shifts to original location
Actual
Drag from left list to right list
Drop item
Item animates to new location, assuming sortable is successful
Item immediately shifts to original location, as sortable was cancelled
Clarification
The revert property moves the item to the location where the item would drop if successful, and then immediately shifts back to the original location due to the revert occurring before the cancel method. Is there a way to alter the life-cycle so if the cancel method is executed, revert isn't, and instead the item is immediately return to it's original location?
i created a demo for you here:
the jsfiddle code
it seems to produce the output you expect.
i changed the receive callback method from this:
$(ui.sender).sortable('cancel');
to this:
$(ui.sender).sortable( "option", "revert", false );
hopefully, this is what you expected.
After many hours for searching for a solution I decided the only way to achieve what I was trying to do was to amend the way in which the jQuery sortable plugin registered the revert time. The aim was to allow for the revert property to not only accept a boolean or integer, but also accept a function. This was achieved by hooking into the prototype on the ui.sortable with quite a lot of ease, and looks something like this.
jQuery Sortable Hotfix
$.ui.sortable.prototype._mouseStop = function(event, noPropagation)
{
if (!event) return;
// if we are using droppables, inform the manager about the drop
if ($.ui.ddmanager && !this.options.dropBehaviour)
$.ui.ddmanager.drop(this, event);
if (this.options.revert)
{
var self = this;
var cur = self.placeholder.offset();
// the dur[ation] will not determine how long the revert animation is
var dur = $.isFunction(this.options.revert) ? this.options.revert.apply(this.element[0], [event, self._uiHash(this)]) : this.options.revert;
self.reverting = true;
$(this.helper).animate({
left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
}, !isNaN(dur) ? dur : 500, function ()
{
self._clear(event);
});
} else
{
this._clear(event, noPropagation);
}
return false;
}
Implementation
$('ul').sortable({
revert: function(ev, ui)
{
// do something here?
return 10;
}
});
I ended up creating a new event called beforeRevert which should return true or false. If false then the cancel function is called and the item is animated back to its original position. I didn't code this with the helper option in mind, so it would probably need some additional work to support that.
jQuery Sortable Hotfix with animation
var _mouseStop = $.ui.sortable.prototype._mouseStop;
$.ui.sortable.prototype._mouseStop = function(event, noPropagation) {
var options = this.options;
var $item = $(this.currentItem);
var el = this.element[0];
var ui = this._uiHash(this);
var current = $item.css(['top', 'left', 'position', 'width', 'height']);
var cancel = $.isFunction(options.beforeRevert) && !options.beforeRevert.call(el, event, ui);
if (cancel) {
this.cancel();
$item.css(current);
$item.animate(this.originalPosition, {
duration: isNaN(options.revert) ? 500 : options.revert,
always: function() {
$('body').css('cursor', '');
$item.css({position: '', top: '', left: '', width: '', height: '', 'z-index': ''});
if ($.isFunction(options.update)) {
options.update.call(el, event, ui);
}
}
});
}
return !cancel && _mouseStop.call(this, event, noPropagation);
};
Implementation
$('ul').sortable({
revert: true,
beforeRevert: function(e, ui) {
return $(ui.item).hasClass('someClass');
}
});

Categories