Scroll page on text input focus for mobile devices? - mobile

Im making a mobile optimised site with a text input which filters a list as you type. Its similar to this: http://jquerymobile.com/test/docs/lists/lists-search.html
For phones, it would be a usability benefit if when you selected the input the page scrolled down so the input was at the top of the page. That way as much of the list below would be visible as you type. Is this possible and/or does anyone have experience doing it? Thanks

Agreed - that'd be nice for usability.
If you're using jQuery, this should do the trick:
$('#id-of-text-input').on('focus', function() {
document.body.scrollTop = $(this).offset().top;
});

A better solution would be:
$('#id-of-text-input').on('focus', function() {
document.body.scrollTop += this.getBoundingClientRect().top - 10
});
Because offsetTop (or .offset().top if using Jquery) returns the distance from the first positioned parent, whereas you need the distance from the top of the document.
getBoundingClientRect().top returns the distance between the current viewport position to the element, u.e. if you're scrolled 300px below the element, it would return -300. So adding the distance using += would scroll to the element. -10 is optional - to allow some space at the top.

$('input, textarea').focus(function () {
$('html, body').animate({ scrollTop: ($('input, textarea').offset().top - 10) }, 1);
return false;
});

Related

How to 1. detect url in textarea event, 2: convert to url. Using AngularJS

We have a simple control (textarea), where we would like to detect user input of 'proper' urls and:
1. Visually convert these 'proper' urls to links and
2. Trigger an event we can hook on to detect a 'proper' link has been entered.
Of course the contents of textareas cannot be styled. As far as we see, though, TinyMCE (via AngularUI) might be the solution along with its 'autolink' plugin. However, other than whitelisting, it is not clear how 1. we can control/manage what a 'proper' link is (for example bit.ly or deliciou.us, etc are common domains now) or 2. how we can detect an event that a proper url has been entered other than using TinyMCE's 'onChange' and using our own custom regular expressions.
Although we can imagine solutions, this is a common requirement nowadays and we're having difficulty finding pre-built solutions to achieve this. BTW, we are using AngularJS.
Any help or pointers greatly appreciated!
Thanks,
Mo
We have a very similar situation where we have a textArea where the user can enter anything and any urls are supposed to be converted and displayed as clickable links. We solved it as follows.
Display the textarea value in a div. Prior to display we translate the value with a small function like this:
$scope.transformHrefsInAnswer = function(value) {
var retval = value;
if(retval != null) {
var urlRegex = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig;
retval = value.replace(urlRegex, function (url) {
return '' + url + '';
});
}
return retval;
};
When the user clicks on the div we switch it to be a editable textarea with the actual value bound to the proper ng-model. They can then put in anything they want. When the area loses focus it switches back to a DIV and we call the transform function again so it displays as a valid, clickable link.
If all you want to do is validate you can use the transform function with minor mods to check for matches.
Hope this helps.

Creating camera image filters with Ionic and Cordova

I'm creating an Instagram clone using Ionic and Cordova. I need to be able to take a picture or select from users library (already set up) and place a set image on top of it (Like a filter that snapchat uses, but another image not just text). However, I am at a loss for how to do this.
This is the function for grabbing the image --
$scope.open = function () {
init();
PhotoService
.open({
quality : GallerySetting.get('imageQuality'),
allowEdit : GallerySetting.get('imageEdit'),
correctOrientation: GallerySetting.get('imageEdit'),
targetWidth : GallerySetting.get('imageWidth'),
targetHeight : GallerySetting.get('imageHeight'),
saveToPhotoAlbum : GallerySetting.get('imageSaveAlbum')
})
.then(function (resp) {
$scope.data = 'data:image/jpeg;base64,' + resp;
$scope.form.photo = resp;
$ionicModal.fromTemplateUrl('module/gallery/view/gallery.capture.modal.html', {
scope : $scope,
focusFirstInput: true
}).then(function (modal) {
$scope.modal = modal;
$scope.modal.show();
});
$scope.closeModal = function () {
$scope.modal.hide();
$scope.modal.remove();
};
})
.catch(function () {
$state.go('gallery.home');
});
};
What would be the best approach for this?
First let's straighten out your understanding of the concept of filters in a programmer's context. Though physical optical filters are literally a partially transparent object put in front of an image, some filters in digital image manipulation cannot be replicated this way. For frames, edge shadows or other artsy things, this might work.
This idea can, however, be done to a certain extent using layers and opacity in HTML5/CSS3/JS, but it will not really be an Instagram clone. There are a lot of image processing options in Instagram you cannot achieve this way. Rather sometimes each pixel in the original image is changed according to some function and copied to the new filtered image. Like this:
Having hand coded something to this effect to this in my past, I can say this type of thing is not terribly easy to do well with current bare minimum APIs. You have to have a relatively good backing in how images are constructed, how to manipulate colors, groups of pixels etc. and have some solid matrix mathematics skills. That said, it is entirely possible using nothing but HTML5 Canvas.
var canvas = document.getElementById('elementId'),
context = canvas.getContext('2d');
var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
for (var i = 0; i < imageData.data.length; i+=4) {
// Manipulate pixels here
}
Check out this example - Using just a few functions, some matrix math and HTML5 canvas, one can process images with some cool and useful effects. Theoretically, these can be used together or alone.
A better option that I hadn't seen before recently is CamanJS. This essentially simplifies the Canvas API for you, and adds a ton of image manipulation functions and the ability to easily save images.
The interactive examples page can give you an idea as to what you can accomplish with this library.

Creating a dynamic pop-up modal with AngularJS

I am trying to create a Service to manage all of the different pop-up modals that will be used in my application. So far everything is going well except for having the ability to update the position of the modal on the screen every time it comes into view.
Here is my UPDATED directive
app.directive('skModal', function($window){
return {
restrict:'EA',
link: function(scope, elem, attrs){
var top, left;
scope.$watch(attrs.ngShow, function(newVal){
if(newVal) {
top = ($window.innerHeight - elem.outerHeight()) / 2;
left = ($window.innerWidth - elem.outerWidth()) / 2;
elem.css({ 'top': top, 'left': left });
}
});
}
} ;
});
I simply use ng-include to populate any template I wish into the modal, then I use $watch to check when the ngShow variable is flipped. The problem is that my elem DOM element is not IN the DOM at the time, so the outerHeight() and outerWidth() functions return 0
Without jQuery you should used angular.element,
angular.element(element).prop('offsetHeight');
You will give the same result
Angular.element does NOT support outerHeight() and height() ;)
I see from your comment that you fixed your issue with a large offset.
In case you want a different solution, you could also try removing ng-hide just before you take your measurement. Given that ngShow has just flipped, you may not need to re-apply it, though it will disrupt animation (if you're using it) if you don't.
scope.$watch(attrs.ngShow, function(newVal){
if(newVal) {
// remove ng-hide to take the measurement
angular.element(elem).removeClass('ng-hide');
top = ($window.innerHeight - elem.outerHeight()) / 2;
left = ($window.innerWidth - elem.outerWidth()) / 2;
// optionally re-apply ng-hide here
elem.css({ 'top': top, 'left': left });
}
});

AngularJS: Attempt to dynamically apply directive using ngClass causing weird functional and performance issues

Requirement
I want a textarea that expands or contracts vertically as the user types, alla Facebook comment box.
When the textarea loses focus it contracts to one line (with ellipsis if content overflows) and re-expands to the size of the entered text upon re-focus (this functionality not found on Facebook)
Note: Clicking on the textarea should preserve caret position exactly where user clicked, which precludes any dynamic swapping of div for textarea as the control receives focus
Attempted Solution
I'm already well into an AngularJS implementation, so...
Use Monospaced's Angular Elastic plugin. Nice.
Two attempts...
Attempt A: <textarea ng-focus="isFocussed=true" ng-blur="isFocussed=false" ng-class="'msd-elastic': isFocussed"></textarea> Fails because ng-class triggers no re-$compile of the element after adding the class, so Angular Elastic is never invoked
Attempt B: create a custom directive that does the needed re-$compile upon class add. I used this solution by hassassin. Fails with the following problems
Attempt B problems
Here's a JSFiddle of Attempt B Note that Angular v1.2.15 is used
I. Disappearing text
go to the fiddle
type into one textarea
blur focus on that textarea (eg click in the other textarea)
focus back on the text-containing textarea
result: text disappears! (not expected or desired)
II. Increasingly excessive looping and eventual browser meltdown
click into one textarea
click into the other one
repeat the above for as long as you can until the browser stops responding and you get CPU 100% or unresponsive script warnings.
you'll notice that it starts out OK, but gets worse the more you click
I confirmed this using: XP/Firefox v27, XP/Chrome v33, Win7/Chrome v33
My investigations so far
It seems that traverseScopesLoop in AngularJS starting at line 12012 of v1.2.15, gets out of control, looping hundreds of times. I put a console.log just under the do { // "traverse the scopes" loop line and clocked thousands of loops just clicking.
Curiously, I don't get the same problems in Angular v1.0.4. See this JSFiddle which is identical, except for Angular version
I logged this as an AngularJS bug and it was closed immediately, because I'd not shown it to be a bug in Angular per se.
Questions
Is there another way to solve this to avoid the pattern in Attempt B?
Is there a better way to implement Attempt B? There's no activity on that stackoverflow issue after hassassin offered the solution I used
Are the problems with Attempt B in my code, angular-elastic, hassassin's code, or my implementation of it all? Appreciate tips on how to debug so I can "fish for myself" in future. I've been using Chrome debug and Batarang for a half day already without much success.
Does it seem sensible to make a feature request to the AngularJS team for the pattern in Attempt A? Since you can add directives by class in Angular, and ngClass can add classes dynamically, it seems only natural to solve the problem this way.
You are severely overthinking this, and I can't think of any reason you would ever need to dynamically add / remove a directive, you could just as easily, inside the directive, check if it should do anything. All you need to do is
Use the elastic plugin you are using
Use your own directive to reset height / add ellipsis when it doesn't have focus.
So something like this will work (not pretty, but just threw it together):
http://jsfiddle.net/ss6Y5/8/
angular.module("App", ['monospaced.elastic']).directive('dynamicClass', function($compile) {
return {
scope: { ngModel: '=' },
require: "?ngModel",
link: function(scope, elt, attrs, ngModel) {
var tmpModel = false;
var origHeight = elt.css('height');
var height = elt.css('height');
var heightChangeIndex = 0;
scope.$watch('ngModel', function() {
if (elt.css('height') > origHeight && !heightChangeIndex) {
heightChangeIndex = scope.ngModel.length;
console.log(heightChangeIndex);
}
else if (elt.css('height') <= origHeight && elt.is(':focus')) {
heightChangeIndex = 0;
}
});
elt.on('blur focus', function() {
var tmp = elt.css('height');
elt.css('height', height);
height = tmp;
});
elt.on('blur', function() {
if (height > origHeight) {
tmpModel = angular.copy(scope.ngModel);
ngModel.$setViewValue(scope.ngModel.substr(0, heightChangeIndex-4) + '...');
ngModel.$render();
}
});
elt.on('focus', function() {
if (tmpModel.length) {
scope.ngModel = tmpModel;
ngModel.$setViewValue(scope.ngModel);
ngModel.$render();
tmpModel = '';
}
});
}
};
})

ng-grid won’t scroll to last row

Using ng-grid I want to show a newly added row by scrolling to it when it is added to the bottom of the list.
please see the plunk: http://plnkr.co/edit/4jgy9wwr2HRhfhgKMKcX?p=preview
var grid = $scope.gridOptions.ngGrid;
grid.$viewport.scrollTop((($scope.myData.length - 1)* grid.config.rowHeight));
I don’t understand why the grid won’t scroll all the way down. I tried adding the rowHeight ever subtracting it but every time the grid just will not scroll to the very last row.
I know by adding the records to the top of the list will solve the problem by I am sorting the grid and by its nature the empty record will be pushed to the bottom.
You dont need the "ngGridEventData", and if you log it you will notice he is fired for each existing row on your grid, wich is not optimal.
As already answered, you can use the $timeout service whith a 0 delay. It sounds a bit tricky, and it's maybe not a best practice but it works.
Load it in your controller :
app.controller('MyCtrl', function($scope, $timeout) {
//load
}
Then in your addNew() :
$scope.addNew = function () {
$scope.myData.push({ name: "new", age: 100 });
$timeout(function () {
var grid = $scope.gridOptions.ngGrid;
$scope.gridOptions.selectItem($scope.myData.length - 1, true);
grid.$viewport.scrollTop((($scope.myData.length - 1) * grid.config.rowHeight));
}, 0);
};
Check the result here : http://plnkr.co/edit/4HNqFh7uu9IWCjDVt5WR?p=preview
Actually Bug lies in ng-grid js file to calculate correct scrolling offset.
Please follow link
http://pushkarkinikar.wordpress.com/2014/07/03/ng-grid-scrolling-issue/
where
I have explained it in detail

Resources