Problem Statement:
I am creating IOS App from Angularjs using ng-cordova plugin,For my project i am using horizontal scroller(like Swipe),Whenever i am trying to select/Deselect the images it's response is getting delay.I used $watch() to watch the changes for my $scope object.I am not able to identify where the problem is raising whether in my css either in my controller.Please any one can help me to clear the issue.
Markup:
<div class="wrapper no-copy">
<div class="internal" ng-repeat="pf in printlist"><img class="" ng-click="pf.selectFile = !pf.selectFile ;showCustom($event,pf)" ng-src="{{pf.imagePath}}"><div class="no-copy filedetail"><div style="color:black;margin-left:7px;" class=" no-copy internal font-filedetail">{{pf.filename.substring(0,8)}}..</br>Pages: {{pf.totalPages}}</br>{{pf.releaseCode}}</div><div class="internal filedetailbar"><img style="height:70px;" src="images/beacon/line_icon.png"></div></div>
</div>
</div>
CSS:
.wrapper {
width: 100%;
white-space: nowrap;
overflow-y: hidden;
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
padding: 1rem;
background-color: white;
// Toggle this depending upon whether you want to see the scrollbar
&::-webkit-scrollbar {
display: none;
}
}
.internal {
display: inline;
}
Controller:
$scope.showCustom = function (event, detail) {
console.log('detail')
console.log(detail)
/*$mdDialog.show({
locals: {name: detail},
clickOutsideToClose: true,
scope: $scope,
preserveScope: true,
templateUrl: 'ibprintProcessList/filedetail.html',
controller: function DialogController($scope, $mdDialog, name) {*/
$scope.del = detail;
console.log($scope.del.color)
console.log('*************')
console.log($scope.del)
if (!$scope.del.color) {
$scope.color = false
}
if ($scope.del.selectFile) {
$scope.filestatus = 'selected '
$scope.disabled = false
$scope.isFileSelect = $scope.isFileSelect + 1
$scope.selectfileextension = $scope.del.filename.split('.')
console.log('???????????????')
console.log($scope.selectfileextension[1])
if (($scope.selectfileextension[1] == 'pdf') || ($scope.selectfileextension[1] == 'rtf')) {
$scope.del.imagePath = "./images/beacon/pdf_icon_select.png"
}
else if (($scope.selectfileextension[1] == 'doc') || ($scope.selectfileextension[1] == 'docx')) {
$scope.del.imagePath = "./images/beacon/word_icon_select.png"
}
else if (($scope.selectfileextension[1] == 'xls') || ($scope.selectfileextension[1] == 'xlsx')) {
$scope.del.imagePath = "./images/beacon/xls_icon_select.png"
}
else if (($scope.selectfileextension[1] == 'ppt') || ($scope.selectfileextension[1] == 'pptx')) {
$scope.del.imagePath = "./images/beacon/pp_icon_select.png"
}
else if (($scope.selectfileextension[1] == 'png') || ($scope.selectfileextension[1] == 'jpg') || ($scope.selectfileextension[1] == 'jpeg') || ($scope.selectfileextension[1] == 'bmp') || ($scope.selectfileextension[1] == 'gif') || ($scope.selectfileextension[1] == 'tiff') || ($scope.selectfileextension[1] == 'tif')) {
$scope.del.imagePath = "./images/beacon/image_icon_select.png"
}
}
else {
$scope.disabled = true
$scope.isFileSelect = $scope.isFileSelect - 1
$scope.filestatus = 'unselected '
$scope.selectfileextension = $scope.del.filename.split('.')
if (($scope.selectfileextension[1] == 'pdf') || ($scope.selectfileextension[1] == 'rtf')) {
$scope.del.imagePath = "./images/beacon/pdf_icon.png"
}
else if (($scope.selectfileextension[1] == 'doc') || ($scope.selectfileextension[1] == 'docx')) {
$scope.del.imagePathh = "./images/beacon/word_icon.png"
}
else if (($scope.selectfileextension[1] == 'xls') || ($scope.selectfileextension[1] == 'xlsx')) {
$scope.del.imagePath = "./images/beacon/xls_icon.png"
}
else if (($scope.selectfileextension[1] == 'ppt') || ($scope.selectfileextension[1] == 'pptx')) {
$scope.del.imagePath = "./images/beacon/pp_icon.png"
}
else if(($scope.selectfileextension[1] == 'png') || ($scope.selectfileextension[1] == 'jpg') || ($scope.selectfileextension[1] == 'jpeg') || ($scope.selectfileextension[1] == 'bmp') || ($scope.selectfileextension[1] == 'gif') || ($scope.selectfileextension[1] == 'tiff') || ($scope.selectfileextension[1] == 'tif')){
$scope.del.imagePath = "./images/beacon/image_icon.png"
}
}
/*$scope.closeDialog = function () {
$mdDialog.hide();
//$scope.printNow()
}*/
$scope.del.$digest()
$scope.printlist.$digest()
console.log('Watch function starts')
// $scope.$watch($scope.del, $scope.showCustom);
$scope.$watchGroup(['$scope.del', '$scope.printlist'],$scope.showCustom,true)
console.log('Watch function ends')
}
When the user Select/Deselect the $scope.showCustom(event,pf) is triggered
pf object is passed to controller and assigned the pf object to $scope.del object
When the user Select/Deselect the $scope.del.imagepath holds the images and changed the image source
Referimage: Screen Shot
I tried both $watch() and $watchGroup() functions it's getting delay on Select/Deselect
Just use a simple horizontal scroller using overflow style,It will work as a SWIPE on your mobile app.
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
}
removed the above CSS ,beacause you are porting from web to mobile app. -webkit-overflow-scrolling is for browser. sometimes it may/maynot work while you converting your web app to mobile app.
Instead that add the below CSS:
overflow-y: hidden;
overflow-x: scroll;
width: 500px; // you can change the width size depends on you
white-space: nowrap;
Related
So I want to make a tic tac toe game with html and script javascript, and my reset button doesn't seem to work, everything else seems to be working well, can anyone tell me what's wrong here?
I've tried moving the restart() function into the body, right after the button, and anywhere else I think possible, and changed the codes in the restart function to almost everything I think works and could be possible, but when I click the button nothing happens
<!DOCTYPE html>
<html>
<head>
<title>Tictactoe Game</title>
<style type="text/css">
[v-cloak] {
display: none;
}
td {
background: #DDD;
width: 50px;
height: 50px;
}
.piece-o {
margin: auto;
width: 30px;
height: 30px;
border: 3px solid #ff8080;
border-radius: 50%;
}
.piece-x {
margin: auto;
width: 30px;
height: 30px;
background: linear-gradient(45deg, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 43%,#0099ff 45%,#0099ff 55%,rgba(0,0,0,0) 57%,rgba(0,0,0,0) 100%), linear-gradient(135deg, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 43%,#0099ff 45%,#0099ff 55%,rgba(0,0,0,0) 57%,rgba(0,0,0,0) 100%);
}
</style>
<script type="text/javascript">
function restart() {
for (i = 0; i <= 2; i++){
for (j = 0; j <= 2; j++){
this.game[i][j] = ' '
}
}
}
</script>
</head>
<body>
<div id="app" v-cloak>
<p>Current Player: <i :class="turn == 'O' ? 'far fa-circle' : 'fas fa-times'"></i></p>
<table>
<tr v-for="(row, rowKey, index) in game" :key="rowKey">
<td v-for="(col, colKey, index) in row" :key="colKey" #click="click(rowKey, colKey)">
<div v-if="col" :class="'piece-'+col.toLowerCase()"></div>
</td>
</tr>
</table>
</div>
<input type="button" onclick=restart() value="Restart">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
turn: 'O',
game: [
['', '', ''],
['', '', ''],
['', '', '']
]
},
methods: {
click(row, col) {
if (this.game[row][col] != '') {
alert('Please select empty slot.')
return
}
this.game[row].splice(col, 1, this.turn);
this.calcResult()
},
calcResult() {
var game = this.game
var turn = this.turn
// Victory Condition
if ((this.game[0][0] == this.game[0][1] && this.game[0][0] == this.game[0][2] && this.game[0][0] != '')
|| (this.game[1][0] == this.game[1][1] && this.game[1][0] == this.game[1][2] && this.game[1][0] != '')
|| (this.game[2][0] == this.game[2][1] && this.game[2][0] == this.game[2][2] && this.game[2][0] != '')
|| (this.game[0][0] == this.game[1][0] && this.game[0][0] == this.game[2][0] && this.game[0][0] != '')
|| (this.game[0][1] == this.game[1][1] && this.game[0][1] == this.game[2][1] && this.game[0][1] != '')
|| (this.game[0][2] == this.game[1][2] && this.game[0][2] == this.game[2][2] && this.game[0][2] != '')
|| (this.game[0][0] == this.game[1][1] && this.game[0][0] == this.game[2][2] && this.game[0][0] != '')
|| (this.game[0][2] == this.game[1][1] && this.game[0][2] == this.game[2][0] && this.game[0][2] != '')
) {
alert('Player ' + this.turn + ' is the winner!');
return;
}
// Draw condition
if (this.game[0][0] != '' && this.game[0][1] && this.game[0][2] && this.game[1][0] && this.game[1][1]
&& this.game[1][2] && this.game[2][0] && this.game[2][1] && this.game[2][2]) {
alert('Draw!');
return;
}
// Next player turn
this.turn = this.turn == 'O' ? 'X' : 'O'
}
}
})
</script>
</body>
</html>
You can change code like this.
<div id="app" v-cloak>
...
</table>
<input type="button" #click="restart()" value="Restart">
</div>
And add restart function to Vue methods because you have to use "game" data of Vue.
...
},
restart() {
for (i = 0; i <= 2; i++){
for (j = 0; j <= 2; j++){
this.game[i][j] = ''
}
}
turn = 'O';
alert("Restart");
}
I imagine it's because your restart() function is outside of the scope of your Vue application. I'm not familiar with Vue, but you probably need to have restart() be a function within app.methods, and call it just like you're calling the other Vue methods. Which also means you'd probably have to have your restart button within the <div id="app" v-cloak> element. You've placed it outside of the parent Vue element at the moment.
I am trying to say that if a URL ends in X, then I want a div to be injected onto the site. However, there are multiple X's.
var adfc = '$0.00';
if (url.indexOf('YHOPT20T', 'YR20B', 'YR20T') > 0) { {
adfc = '$10.00';
}
if (adfc == '$0.00') {
if (shipsfree == true && returnsfree == true) {
document.write('<div style="padding-top:12px;"><span class="shipNote">FREE SHIPPING & FREE RETURN</span></div>');
sizeadviceshow = true;
}
else if (shipsfree == true && returnsfree == false) {
document.write('<div style="padding-top:12px;"><span class="shipNote">FREE SHIPPING</span></div>');
}
}
else {
document.write('<div style="padding-top:12px;"><span style="font-size:12px; font-weight:normal; color:#AA1E37; cursor:pointer; line-height:18px;">' + adfc + ' Additional Freight Charge</span></div>');
}
How would I accomplish targeting multiple sections in the code?
There are spans in the content editable div which needs to be deleted all at once. Now this works fine on the chrome desktop but when I try to delete it on the following devices this does not works.I am testing it on following
Chrome on Android
Safari on Mac, iOS
Firefox on Windows
When I try to delete the box looses focus and span does not delete.
This is the fiddle link to what I have tried so far.
var EditableDiv = document.getElementById('EditableDiv');
EditableDiv.onkeydown = function(event) {
var ignoreKey;
var key = event.keyCode || event.charCode;
if (!window.getSelection) return;
var selection = window.getSelection();
var focusNode = selection.focusNode,
anchorNode = selection.anchorNode;
var anchorOffset = selection.anchorOffset;
if (!anchorNode) return
if (anchorNode.nodeName.toLowerCase() != '#text') {
if (anchorOffset < anchorNode.childNodes.length)
anchorNode = anchorNode.childNodes[anchorOffset]
else {
while (!anchorNode.nextSibling) anchorNode = anchorNode.parentNode // this might step out of EditableDiv to "justincase" comment node
anchorNode = anchorNode.nextSibling
}
anchorOffset = 0
}
function backseek() {
while ((anchorOffset == 0) && (anchorNode != EditableDiv)) {
if (anchorNode.previousSibling) {
if (anchorNode.previousSibling.nodeName.toLowerCase() == '#text') {
if (anchorNode.previousSibling.nodeValue.length == 0)
anchorNode.parentNode.removeChild(anchorNode.previousSibling)
else {
anchorNode = anchorNode.previousSibling
anchorOffset = anchorNode.nodeValue.length
}
} else if ((anchorNode.previousSibling.offsetWidth == 0) && (anchorNode.previousSibling.offsetHeight == 0))
anchorNode.parentNode.removeChild(anchorNode.previousSibling)
else {
anchorNode = anchorNode.previousSibling
while ((anchorNode.lastChild) && (anchorNode.nodeName.toUpperCase() != 'SPAN')) {
if ((anchorNode.lastChild.offsetWidth == 0) && (anchorNode.lastChild.offsetHeight == 0))
anchorNode.removeChild(anchorNode.lastChild)
else if (anchorNode.lastChild.nodeName.toLowerCase() != '#text')
anchorNode = anchorNode.lastChild
else if (anchorNode.lastChild.nodeValue.length == 0)
anchorNode.removeChild(anchorNode.lastChild)
else {
anchorNode = anchorNode.lastChild
anchorOffset = anchorNode.nodeValue.length
//break //don't need to break, textnode has no children
}
}
break
}
} else
while (((anchorNode = anchorNode.parentNode) != EditableDiv) && !anchorNode.previousSibling) {}
}
}
if (key == 8) { //backspace
if (!selection.isCollapsed) {
try {
document.createElement("select").size = -1
} catch (e) { //kludge for IE when 2+ SPANs are back-to-back adjacent
if (anchorNode.nodeName.toUpperCase() == 'SPAN') {
backseek()
if (anchorNode.nodeName.toUpperCase() == 'SPAN') {
var k = document.createTextNode(" ") // doesn't work here between two spans. IE makes TWO EMPTY textnodes instead !
anchorNode.parentNode.insertBefore(k, anchorNode) // this works
anchorNode.parentNode.insertBefore(anchorNode, k) // simulate "insertAfter"
}
}
}
} else {
backseek()
if (anchorNode == EditableDiv)
ignoreKey = true
else if (anchorNode.nodeName.toUpperCase() == 'SPAN') {
SelectText(event, anchorNode)
ignoreKey = true
} else if ((anchorNode.nodeName.toLowerCase() == '#text') && (anchorOffset <= 1)) {
var prev, anchorNodeSave = anchorNode,
anchorOffsetSave = anchorOffset
anchorOffset = 0
backseek()
if (anchorNode.nodeName.toUpperCase() == 'SPAN') prev = anchorNode
anchorNode = anchorNodeSave
anchorOffset = anchorOffsetSave
if (prev) {
if (anchorOffset == 0)
SelectEvent(prev)
else {
var r = document.createRange()
selection.removeAllRanges()
if (anchorNode.nodeValue.length > 1) {
r.setStart(anchorNode, 0)
selection.addRange(r)
anchorNode.deleteData(0, 1)
}
else {
for (var i = 0, p = prev.parentNode; true; i++)
if (p.childNodes[i] == prev) break
r.setStart(p, ++i)
selection.addRange(r)
anchorNode.parentNode.removeChild(anchorNode)
}
}
ignoreKey = true
}
}
}
}
if (ignoreKey) {
var evt = event || window.event;
if (evt.stopPropagation) evt.stopPropagation();
evt.preventDefault();
return false;
}
}
function SelectText(event, element) {
var range, selection;
EditableDiv.focus();
if (window.getSelection) {
selection = window.getSelection();
range = document.createRange();
range.selectNode(element)
selection.removeAllRanges();
selection.addRange(range);
} else {
range = document.body.createTextRange();
range.moveToElementText(element);
range.select();
}
var evt = (event) ? event : window.event;
if (evt.stopPropagation) evt.stopPropagation();
if (evt.cancelBubble != null) evt.cancelBubble = true;
return false;
}
#EditableDiv {
height: 75px;
width: 500px;
font-family: Consolas;
font-size: 10pt;
font-weight: normal;
letter-spacing: 1px;
background-color: white;
overflow-y: scroll;
overflow-x: hidden;
border: 1px solid black;
padding: 5px;
}
#EditableDiv span {
color: brown;
font-family: Verdana;
font-size: 8.5pt;
min-width: 10px;
/*_width: 10px;*/
/* what is this? */
}
#EditableDiv p,
#EditableDiv br {
display: inline;
}
<div id="EditableDiv" contenteditable="true">
(<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field1</span> < 500) <span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>OR</span> (<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field2</span> > 100 <span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>AND</span> (<span contenteditable='false' onclick='SelectText(event, this);' unselectable='on'>Field3</span> <= 200) )
</div>
I am using javascript for this.
I am developing a solution for a app using meteor and blaze UI for templates which will run on iOS.
Try to put the css cursor:pointer; on the div that you want to click on and delete.
Sometimes mobile browser, esp. Safari doesnt allow click event on the element unless it has cursor:pointer. It will trigger that this div is link or clickable.
Thanks in advance for your attention,
I'm using the W3 PHP AJAX Live Search Example and it's already integrated on this site. It's just about perfect. I wish to use arrows on keyboard, up (or left) and down (or right), to focus results inside of <div id="livesearch">. Than, on focus press Enter ⏎ key to load.
In HTML head :
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<style>
#livesearch {
min-height: 155px;
}
#livesearch a:hover {
text-decoration: none;
background-color: rgba(0,0,0,0.05);
}
#livesearch a {
text-transform: capitalize;
font-size: inherit;
padding: 5px 13px;
display: block;
}
#livesearch .selected {
text-decoration: none;
background-color: rgba(0,0,0,0.05);
}
</style>
</head>
HTML Form :
<body>
<form method="post" id="myfrm">
<input type="text" name="search" class="form-control search" placeholder="Just start typing..." autofocus="">
</form>
<div id="livesearch"><div>
</body>
AJAX function :
<script>
function showResult(str) {
if (str.length==0) {
document.getElementById("livesearch").innerHTML="";
document.getElementById("livesearch").style.border="0px";
return;
}
if (window.XMLHttpRequest) {
xmlhttp=new XMLHttpRequest();
} else {
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function() {
if (this.readyState==4 && this.status==200) {
document.getElementById("livesearch").innerHTML=this.responseText;
}
}
xmlhttp.open("GET","livesearch.php?q="+str,true);
xmlhttp.send();
}
</script>
Jquery :
<script>
$(document).ready(function ($) {
$('.search').keyup(function (e) {
var key = e.keyCode;
if (key == 40 || key == 38 || key == 13) {
return false;
}
var str = $('.search').val();
showResult(str);
});
$('#myfrm').on("keydown", ".search", function (e) {
var $listItems = $('#livesearch a');
var key = e.keyCode,
$selected = $listItems.filter('.selected'),
$current;
if (key != 40 && key != 38 && key != 13)
return;
//$listItems.removeClass('selected');
if (key == 40) // Down key
{
$listItems.removeClass('selected');
if (!$selected.length || $selected.is(':last-child')) {
$current = $listItems.eq(0);
} else {
$current = $selected.next();
}
console.log("Current : "+$current);
}
else if (key == 38) // Up key
{
$listItems.removeClass('selected');
if (!$selected.length || $selected.is(':first-child')) {
$current = $listItems.last();
} else {
$current = $selected.prev();
}
}
else if (key == 13) // Enter key
{
$current = $listItems.filter('.selected');
$current.trigger('click');
return false;
}
$current.addClass('selected');
});
});
</script>
Retrieve data in input search box from livesearch data :
<script>
$(document).ready(function ($) {
$("body").on("click", "#livesearch a", function(e){
e.preventDefault();
var data = $(this).text();
$(".search").val(data);
$('#livesearch').html('');
});
});
</script>
If you want used instead of ajax showResult(str) using ajax+jquery for data retrieve livesearch.php so, you can used bellow code :
<script>
$(document).ready(function ($) {
$('.search').keyup(function (e) {
var key = e.keyCode;
if (key == 40 || key == 38 || key == 13) {
return false;
}
var str = $('.search').val();
$.ajax({
context: this,
url: 'livesearch.php',
type: 'get',
dataType: 'html',
data: {
q: str,
},
beforeSend: function () {
console.log("Loadding...");
}
}).done(function (response) {
$("#livesearch").html(response);
});
});
});
</script>
document.getElementById("yourtextfield").addEventListener("keyup",function(event){
var livesearchelem = document.getElementById("livesearch");
var childrens = livesearchelem.getElementsByTagName("a"); //Get only hyperlinks
var key = event.keyCode;
var selected = this.selectedResultNumber;
if (key == 38){ //Arrow up
if (childrens.length === 0){ return; }
if (!selected){ //If 'selectedResultNumber' is undefined
childrens[childrens.length - 1].style.backgroundColor = 'blue';
childrens[childrens.length - 1].style.color = 'white';
//Store the selected number into this element
this.selectedResultNumber = childrens.length - 1;
}
else if (selected > 1){
//Restore the previous selected element's style
childrens[selected - 1].style.backgroundColor = 'white';
childrens[selected - 1].style.color = 'black';
//Set the new selected element's style
childrens[selected - 2].style.backgroundColor = 'blue';
childrens[selected - 2].style.color = 'white';
//Decrease the selected number by 1
this.selectedResultNumber--;
}
}
else if (key == 40){ //Arrow down
if (childrens.length === 0){ return; }
if (!selected){ //If 'selectedResultNumber' is undefined
childrens[0].style.backgroundColor = 'blue';
childrens[0].style.color = 'white';
//Store the selected number into this element
this.selectedResultNumber = 1;
}
else if (selected < childrens.length){
//Restore the previous selected element's style
childrens[selected - 1].style.backgroundColor = 'white';
childrens[selected - 1].style.color = 'black';
//Set the new selected element's style
childrens[selected].style.backgroundColor = 'blue';
childrens[selected].style.color = 'white';
//Increase the selected number by 1
this.selectedResultNumber++;
}
}
else if (key == 13){ //Enter key
if (childrens.length === 0){ return; }
//Trigger click event on the selected element
childrens[selected - 1].click();
}
else{ //Searching in progress
delete this.selectedResultNumber;
//Your search function goes here
}
});
I'm trying to implement a 'Tag Editor' field in my app, the same way as SO does.
Right now i got this:
EDIT: I have coded this into a jQuery plugin at:
https://github.com/fernandotenorio/tagme.git
Fiddle
http://jsfiddle.net/FernandoTen/PnYuF/
html
<div style="margin-left: auto; margin-right: auto; width: 400px;">
<span style='color: #333; font-size: small'>
Tags [a-z A-Z 0-9 # + . -]
</span>
<div id='tags_container'>
<span id='tags_queue'></span>
<input type='text' id='tf' />
</div>
</div>
js
$(document).ready(function () {
var rexp = /[^a-zA-Z0-9#+\.\-]/
var left = 37;
var right = 39;
var del = 8;
var space = 32;
var comma = 188;
var minlen = 3;
var maxlen = 15;
var current_tag = null
$('#tf').focus().attr('maxlength', maxlen)
$('#tags_container').click(function () {
$('#tf').focus()
})
$('#tf').keydown(function (e) {
var code = e.keyCode ? e.keyCode : e.which ? e.which : e.charCode;
var txt = $(this).val().trim()
// handle navigation between tags
if ((code === left || code === right) && txt.length === 0) {
if (!current_tag)
{
if (code === left)
current_tag = $('#tags_queue span').last()
else
current_tag = $('#tags_queue span').first()
if (current_tag.length > 0) current_tag.css('background-color', '#9FC2D6')
} else //current tag exists
{
if (code === left)
current_tag = current_tag.css('background-color', '#B8D0DE').prev()
else
current_tag = current_tag.css('background-color', '#B8D0DE').next()
if (current_tag.length > 0)
current_tag.css('background-color', '#9FC2D6')
// hit last/first, clean current_tag
else
{
current_tag.css('background-color', '#B8D0DE')
current_tag = null
}
}
}
});
$('#tf').keypress(function (e) {
var token = String.fromCharCode(e.which)
var code = e.keyCode ? e.keyCode : e.which ? e.which : e.charCode;
if (token.match(rexp) && (code !== del) && (code !== left) && (code !== right))
return false;
});
$('#tf').keyup(function (e) {
var code = e.keyCode ? e.keyCode : e.which ? e.which : e.charCode;
var txt = $(this).val().trim()
if (code === del && txt.length === 0 && current_tag) {
current_tag.remove()
current_tag = null
return
}
// clean current_tag, user is typing
if (current_tag && (code !== left) && (code != right))
{
current_tag.css('background-color', '#B8D0DE')
current_tag = null
}
if (txt.length > maxlen) txt = txt.substring(0, maxlen)
if (txt.match(rexp)) {-
$(this).val("")
return
} else if (code === space || code === comma) {
if (txt.length < minlen) return;
var tag = $('<span></span>', {
'class': 'tag'
}).html(txt).append($('<a></a>', {
'class': 'close_tag',
html: '×',
click: function () {
$(this).parent().fadeOut(function(){
$(this).remove()
current_tag.css('background-color', '#B8D0DE')
current_tag = null
})
}
}))
$('#tags_queue').append(tag)
$(this).val("")
}
});
})
css
div#tags_container {
border: 1px solid #ccc;
padding-bottom: 5px;
border-radius: 5px;
background-color: beige;
overflow: hidden;
}
input#tf {
border: 1px solid orange !important;
outline: none !important;
background-color: transparent !important;
height: 30px;
margin-top: 2px;
padding-left: 5px;
}
a.close_tag {
margin-left: 5px;
color: #fff;
}
a.close_tag:hover {
cursor: pointer;
}
span.tag {
background-color: #B8D0DE;
color: #111;
margin-left: 5px;
margin-top: 5px;
padding: 5px;
float: left;
border-radius: 5px;
}
span.tag:hover {
background-color: #9FC2D6;
}
span#tags_queue {
margin-right: 5px;
vertical-align: middle;
}
The problem is, how can i handle the 'hidden' content using the arrows keys, the way SO does? It looks like i have to handle the cursor position inside the text-fields, and watch for arrow-left and arrow-right events...and there's also the click on the completed tags.
I can think of the following (anti-SO design) solutions:
remove white-space: nowrap from the tags_container div, and let
the div 'grow' as the user add more tags.
Add overflow: scroll to the tags_container div (very ugly!)
So, i appreciate any new ideas on how to approximate the SO tag-editor behaviour.
Thanks!
Reinvent the wheel is a bad practice : take a look at the excellent Tag-it jQuery plugin code, it implements the stuff you are working on.