Angular: Empty Fiddle
http://angularjs.org/
HTML
<script src="http://code.angularjs.org/angular-1.0.1.js"></script>
<div ng-controller="MyCtrl">
Hello, {{name}}!
<auto-expand-input />
</div>
<a href="#">Focusable link</a>
CSS
/*
The burden is on you to make sure that the styles match so that
when the textarea swaps in it's seamless
*/
input[type="text"],
textarea {
width: 20%;
padding: .3em;
line-height: 1.5;
border: 1px solid red;
border-bottom-color: blue;
background-color: burlywood;
box-sizing: border-box;
overflow: hidden;
font-size: 2em;
resize: none;
}
input[type="text"] {
position: relative;
z-index: 1;
/* Uncomment this opacity when you're ready
opacity: 0;
*/
}
textarea {
position: absolute;
z-index: 0;
}
textarea:focus {
z-index: 2
}
textarea:focus + input[type="text"] {
opacity: 0;
}
JavaScript
var myApp = angular.module('myApp',[]);
myApp.directive('autoExpandInput', function() {
return {
restrict: 'E',
replace: 'true',
template: '' +
'<div>' +
'<textarea rows="1" tabindex="-1"></textarea>' +
'<input type="text" value="{{inputValue}}" />' +
'</div>',
link: function(scope, elem, attrs) {
elem.find('input').bind('focus', function (ev) {
ev.target.tabIndex = -1;
ev.target.previousSibling.focus();
});
elem.find('textarea').bind('blur', function (ev) {
setTimeout(function () {
ev.target.nextSibling.tabIndex = 0;
});
})[0].oninput = function (ev) {
var textarea = ev.target;
scope.$apply(function() {
scope.inputValue = textarea.value;
});
textarea.style.height = "";
if (textarea.clientHeight < textarea.scrollHeight + 2) {
textarea.style.height = textarea.scrollHeight + 2 + "px";
textarea.nextSibling.style.height = textarea.scrollHeight + 2 + "px";
}
}
}
};
});
function MyCtrl($scope) {
$scope.name = 'Superhero';
$scope.inputValue = "";
}