Here is my first template:
<script type="text/html" id="Testing">
<!-- ko if: $data.CLASSIFICATION_NAME().toLowerCase() == 'general' -->
<!-- ko if: $data.ATTRIBUTE_DISPLAY_TYPE().toLowerCase() == AttributeEnum.NumberRange -->
<li>
<div class="divFilterRow">
<div class="divFilterCol">
<input type="checkbox" data-bind="checked: ISATTRIBUTESELECTED, attr:{'id': ATTRIBUTE_NAME}, click: function(){checkedCallback($data); return true; }" />
</div>
<div class="divFilterCol divFilterCph">
<label data-bind=" text: ATTRIBUTE_NAME, attr:{'for': ATTRIBUTE_NAME,'attributetooltip': ATTRIBUTE_DESCRIPTION}"></label>
<a class="iClose" href="javascript:void(0);" data-bind=" click: $data.removeSelectedAttribute , style: {display: ISATTRIBUTESELECTED() ? 'block' : 'none'}"></a>
</div>
</div>
<div class="iClearBoth" />
<div data-bind=" visible: ISATTRIBUTESELECTED">
<div data-bind=" template: {name: 'sliderTemplate',foreach: filterSliderValues} "></div>
</div>
</li>
<!-- /ko -->
<!-- ko if: $data.ATTRIBUTE_DISPLAY_TYPE().toLowerCase() == AttributeEnum.MultipleChoiceList -->
<li>
<div class="divFilterRow">
<div class="divFilterCol">
<input type="checkbox" data-bind="checked: ISATTRIBUTESELECTED, attr:{'id': ATTRIBUTE_NAME}, click: function(){checkedCallback($data); return true; }" />
</div>
<div class="divFilterCol divFilterCph">
<label data-bind="text: ATTRIBUTE_NAME, attr:{'for': ATTRIBUTE_NAME,'attributetooltip': ATTRIBUTE_DESCRIPTION }"></label>
<a class="iClose" href="javascript:void(0);" data-bind="click: $data.removeSelectedAttribute , style: {display: ISATTRIBUTESELECTED() ? 'block' : 'none'}"></a>
</div>
</div>
<div class="iClearBoth" />
<div data-bind="visible: ISATTRIBUTESELECTED">
<div data-bind=" template: {name: 'sliderTemplate',foreach: filterSliderValues} "></div>
</div>
<div data-bind="visible: ISATTRIBUTESELECTED">
<div data-bind="dialog: {'autoOpen': false, 'title': ATTRIBUTE_NAME,'modal': true,
'resizable': false, width: 950, draggable: false, closeOnEscape: false
#*,buttons: {
'Apply': function () {
$(this).dialog('close');
}
} *#
},
dialogVisible: isDialogOpen"
class="iSmallDialog" >
<div class="HelpBar" style="display: block; margin-bottom: 12px;">
<div class="divHelpContent" data-bind="text: ATTRIBUTE_DESCRIPTION">
</div>
</div>
<div style="padding-top: 5px; padding-bottom: 10px;">
Please select options from the list below:
</div>
<div style="overflow-y: auto; max-height: 200px;" class="ListOptions">
<ul data-bind="sortable: { template: 'TestingTemplate', data: filterListOptions },class:'ui-sortable'"></ul>
</div>
<div class="iDialogSubmit">
<button data-bind="click: $data.onDialogCloseCallback,enable: isOptionsSelected" class="active">TestingApply</button>
<button class="btnInactive" data-dismiss="modal" data-bind="click: $data.onDialogClose" aria-hidden="true">TestingCancel</button>
</div>
</div>
</div>
</li>
<!-- /ko -->
<!-- /ko -->
</script>
Here is my second template:
<script id="TestingTemplate" type="text/html">
<li><label data-bind="text: Value, attr:{'for': Value().replace(/ /g,'_')}"></label></li>
</script>
Here is the way i call it:
<div class="LHSListItem">General</div>
<div class="contentDivWithPadding fixHeightAccordian">
<div class="divAccordianContent" id="divAttributes_General">
<ul data-bind="template: { name : 'Testing', foreach : filterData} "></ul>
</div>
</div>
</li>
Here is my output:
When i drag Active or Out of Study,output becomes as follow:
Here is My Model:
var extendedAttributeMapping = {
//'ignore': ["ISATTRIBUTESELECTED"],
create: function (options) {
var extendedAttributeModel = ko.mapping.fromJS(options.data);
//drop down for SECTION attribute
extendedAttributeModel.selectedFilterStateforSection = ko.observable();
// extendedAttributeModel.sectionCounter = ko.observable("2509");
extendedAttributeModel.removeSelectedAttribute = function (modelObject) {
modelObject.ISATTRIBUTESELECTED(false);
if(modelObject.ATTRIBUTE_DISPLAY_TYPE().toLowerCase() == AttributeEnum.MultipleChoiceList)
{
modelObject.isDialogOpen(false);
extendedAttributeModel.onAttributeUncheckCallback(modelObject);
//modelObject.OptionsCountLabel("");
//modelObject.isOptionsSelected(false);
//modelObject.OptionsSelectedCount(0);
}
if(modelObject.ATTRIBUTE_DISPLAY_TYPE().toLowerCase() == AttributeEnum.NumberRange)
{
extendedAttributeModel.resetSlider(modelObject);
}
sltModel.getsectionCount();
$(document).trigger('OnApplyAttribute');
};
extendedAttributeModel.resetSlider = function(modelObject){
var minCurrentVal = modelObject.filterSliderValues()[0].MINCURRENT();
var maxCurrentVal = modelObject.filterSliderValues()[0].MAXCURRENT();
modelObject.filterSliderValues()[0].min(minCurrentVal);
modelObject.filterSliderValues()[0].max(maxCurrentVal);
};
//jquery UI Dialog custom Helper events
extendedAttributeModel.onDialogCloseCallback = function(modelObject){
//alert("Callback");
//debugger;
modelObject.isDialogOpen(false);
if(modelObject.ATTRIBUTE_CODE().toLowerCase() == "section")
{
extendedAttributeModel.getSectionAttributeOptionsOnApply(modelObject);
}
else
{
extendedAttributeModel.getOptionsOnApply(modelObject);
}
sltModel.getsectionCount();
extendedAttributeModel.setDefaultModified(modelObject);
$(document).trigger('OnApplyAttribute');
};
extendedAttributeModel.onDialogOpen = function(){
extendedAttributeModel.isDialogOpen(true);
//modelObject.isDialogOpen(true);
//modelObject.ISATTRIBUTESELECTED(true);
};
//SECTION ATTRIBUTE IMPLEMENETATION- EXTENDED MODEL
//SECTION CHECKBOXES SELECTION
extendedAttributeModel.getSectionAttributeOptionsOnApply = function(modelObject) {
var totalSelected = 0;
var selectedAttributeID = modelObject.ATTRIBUTE_ID();
ko.utils.arrayForEach(modelObject.filterddlSectionListOptions(), function(outeritem) {
// sltModel.setSectionAttributeOriginalStateName(outeritem);
if(outeritem.sectionList().length > 0)
{
ko.utils.arrayForEach(outeritem.sectionList(),function(item){
if (item.isSectionSelected()) {
totalSelected++;
// outeritem.isSelected(true);
//highlight selected states
}
item.SavedSectionCheckedState(item.isSectionSelected());
});
}
});
extendedAttributeModel.OptionsCountLabel("(" + totalSelected + ")");
extendedAttributeModel.OptionsSelectedCount(totalSelected);
if(totalSelected > 0)
extendedAttributeModel.isOptionsSelected(true);
else
{
extendedAttributeModel.isOptionsSelected(false);
extendedAttributeModel.ISATTRIBUTESELECTED(false);
}
};
//ali method code starts
extendedAttributeModel.isTaskSelected = function(task) {
return task === self.selectedTask();
};
//ali method code ends
extendedAttributeModel.OnSectionAttributeOptionSelect = function(modelObject,parentObject){
//SavedSectionCheckedState
//isSectionModified
//isSectionSelected
//sectionId
//sectionName
//stateCode
modelObject.isSectionModified(true);
var totalSelected=0;
ko.utils.arrayForEach(parentObject.filterddlSectionListOptions(), function(outeritem) {
if(outeritem.sectionList().length > 0)
{
ko.utils.arrayForEach(outeritem.sectionList(),function(item){
if (item.isSectionSelected()) {
totalSelected++;
}
});
}
});
//sections selected per state
var sectionSelectedPerState=0;
ko.utils.arrayForEach(parentObject.filterddlSectionListOptions()[sltModel.SectionAttributeStateIndex()].sectionList(), function(item) {
if (item.isSectionSelected()) {
sectionSelectedPerState++;
}
});
parentObject.filterddlSectionListOptions()[sltModel.SectionAttributeStateIndex()].sectionSelectedCount = (sectionSelectedPerState);
var indexCounter = parentObject.filterddlSectionListOptions()[sltModel.SectionAttributeStateIndex()].Text().indexOf("(");
var stateNameWithCount = 0;
if(indexCounter > -1)
{
stateNameWithCount = parentObject.filterddlSectionListOptions()[sltModel.SectionAttributeStateIndex()].Text().substring(0,indexCounter);
}
else{
stateNameWithCount = parentObject.filterddlSectionListOptions()[sltModel.SectionAttributeStateIndex()].Text();
}
if(sectionSelectedPerState == 0)
parentObject.filterddlSectionListOptions()[sltModel.SectionAttributeStateIndex()].Text($.trim(stateNameWithCount));
else
parentObject.filterddlSectionListOptions()[sltModel.SectionAttributeStateIndex()].Text($.trim($.trim(stateNameWithCount) + " ("+sectionSelectedPerState+")"));
//sections selected per state
if(totalSelected > 0)
{
parentObject.isOptionsSelected(true);
}
else
{parentObject.isOptionsSelected(false);}
};
extendedAttributeModel.getSectionAttributeOptionsSelectedCount = function(modelObject) {
var totalSelected = 0;
var perStateCounter = 0;
var selectedAttributeID = modelObject.ATTRIBUTE_ID();
ko.utils.arrayForEach(modelObject.filterddlSectionListOptions(), function(outeritem) {
if(outeritem.sectionList().length > 0)
{
ko.utils.arrayForEach(outeritem.sectionList(),function(item){
//if user unchecks checked and press cancel
if(item.isSectionModified()) {
item.isSectionSelected(item.SavedSectionCheckedState());
//if(!item.SavedSectionCheckedState())
//sltModel.setSectionAttributeOriginalStateName(outeritem);
}
if (item.isSectionSelected()) {
totalSelected++;
perStateCounter++;
}
});
}
sltModel.setSectionAttributeOriginalStateName(outeritem,perStateCounter);
perStateCounter = 0;
});
//setting the state name for selected sections
extendedAttributeModel.OptionsCountLabel("(" + totalSelected + ")");
extendedAttributeModel.OptionsSelectedCount(totalSelected);
if(totalSelected > 0)
extendedAttributeModel.isOptionsSelected(true);
else
{
extendedAttributeModel.isOptionsSelected(false);
extendedAttributeModel.ISATTRIBUTESELECTED(false);
}
};
//END OF SECTION ATTRIBUTE IMPLEMENETATION- EXTENDED MODEL
extendedAttributeModel.OnOptionSelect = function(modelObject,parentObject){
modelObject.isModified(true);
var totalSelected=0;
ko.utils.arrayForEach(parentObject.filterListOptions(), function(item) {
if(modelObject.childObjects()==null)
{
if (item.isSelected()) {
totalSelected++;
}
}
});
//Get All the child elements for the selected item
//if the selected option is checked then check all the child options
//else vice versa
if(modelObject.childObjects()!=null){
var isChecked = modelObject.isSelected();
ko.utils.arrayForEach(modelObject.childObjects(), function(item) {
//item.isModified(true);
item.isSelected(isChecked);
totalSelected++;
});
}
if(totalSelected > 0)
{
parentObject.isOptionsSelected(true);
}
else
{parentObject.isOptionsSelected(false);}
};
//new event added for the child options
extendedAttributeModel.OnChildOptionSelect = function(modelObject,parentContext, parentContextParentObject, parentObject){
modelObject.isModified(true);
var totalChildSelected=0;
ko.utils.arrayForEach(parentObject.childObjects(), function(item) {
if (item.isSelected())
{
totalChildSelected++;
}
});
if(totalChildSelected == parentObject.childObjects().length)
{
parentObject.isSelected(true);
}
else
{
parentObject.isSelected(false);
}
if(totalChildSelected > 0)
{
parentContextParentObject.isOptionsSelected(true);
}
else
{
parentContextParentObject.isOptionsSelected(false);
}
};
//eof child option event
extendedAttributeModel.setDefaultModified = function(modelObject){
if(modelObject.ATTRIBUTE_CODE().toLowerCase() == "section")
{
ko.utils.arrayForEach(modelObject.filterddlSectionListOptions(), function(outeritem) {
if(outeritem.sectionList().length > 0)
{
ko.utils.arrayForEach(outeritem.sectionList(),function(item){
item.isSectionModified(false);
});
}
});
}
else
{
ko.utils.arrayForEach(modelObject.filterListOptions(),function(elem){
elem.isModified(false);
});
}
};
//instead of using click event , binding applied to observable property itself to show PopUp
// extendedAttributeModel.ISATTRIBUTESELECTED.subscribe(function(value) {
extendedAttributeModel.checkedCallback = function(modelObject,SOURCE){
if(SOURCE == "LABEL")
{
modelObject.ISATTRIBUTESELECTED(true);
}
if(modelObject.ATTRIBUTE_DISPLAY_TYPE().toLowerCase() == AttributeEnum.MultipleChoiceList)
{
extendedAttributeModel.setDefaultModified(modelObject);
if(modelObject.ISATTRIBUTESELECTED())
{
modelObject.isDialogOpen(true);
//if(modelObject.ATTRIBUTE_DISPLAY_TYPE.toLowerCase()=="list")
//{
//}
}
else
{
modelObject.isDialogOpen(false);
extendedAttributeModel.onAttributeUncheckCallback(modelObject);
sltModel.getsectionCount();
$(document).trigger('OnApplyAttribute');
}
}
if(modelObject.ATTRIBUTE_DISPLAY_TYPE().toLowerCase() == AttributeEnum.NumberRange)
{
if(!modelObject.ISATTRIBUTESELECTED())
{
extendedAttributeModel.resetSlider(modelObject);
}
sltModel.getsectionCount();
$(document).trigger('OnApplyAttribute');
}
};
// },extendedAttributeModel);
extendedAttributeModel.onDialogClose = function(modelObject){
//alert("Close");
//debugger;
modelObject.isDialogOpen(false);
if(modelObject.ATTRIBUTE_CODE().toLowerCase() == "section")
{
extendedAttributeModel.getSectionAttributeOptionsSelectedCount(modelObject);
}
else
{
extendedAttributeModel.getOptionsSelectedCount(modelObject);
}
};
//Helper functions for label count selected Options
extendedAttributeModel.OptionsCountLabel = ko.observable();
extendedAttributeModel.OptionsSelectedCount = ko.observable();
extendedAttributeModel.isOptionsSelected = ko.observable();
extendedAttributeModel.getOptionsSelectedCount = function(modelObject) {
debugger;
var totalSelected = 0;
var selectedAttributeID = modelObject.ATTRIBUTE_ID();
ko.utils.arrayForEach(modelObject.filterListOptions(), function(item) {
//Checkbox is checked but cancel button clicked
//if (item.isSelected() && item.isModified()) {
// item.isSelected(item.SavedCheckedState());
//}
//if user unchecks checked and press cancel
if(item.isModified()) {
item.isSelected(item.SavedCheckedState());
}
if (item.isSelected()) {
totalSelected++;
}
});
extendedAttributeModel.OptionsCountLabel("(" + totalSelected + ")");
extendedAttributeModel.OptionsSelectedCount(totalSelected);
if(totalSelected > 0)
extendedAttributeModel.isOptionsSelected(true);
else
{
extendedAttributeModel.isOptionsSelected(false);
extendedAttributeModel.ISATTRIBUTESELECTED(false);
}
};
extendedAttributeModel.getOptionsOnApply = function(modelObject) {
debugger;
var totalSelected = 0;
var selectedAttributeID = modelObject.ATTRIBUTE_ID();
var i=0;
ko.utils.arrayForEach(modelObject.filterListOptions(), function(item) {
if(modelObject.filterListOptions()[i].childObjects()==null)
{
if (item.isSelected()) {
totalSelected++;
}
}
//Get All the child elements for the selected item
//if the selected option is checked then check all the child options
//else vice versa
if(modelObject.filterListOptions()[i].childObjects()!=null){
//var isChecked = modelObject.filterListOptions()[i].isSelected();
//if(isChecked){
ko.utils.arrayForEach(modelObject.filterListOptions()[i].childObjects(), function(item) {
if( item.isSelected())
{
totalSelected++;
}
});
//}
}
//if (item.isSelected()) {
// totalSelected++;
//}
item.SavedCheckedState(item.isSelected());
i++;
});
extendedAttributeModel.OptionsCountLabel("(" + totalSelected + ")");
extendedAttributeModel.OptionsSelectedCount(totalSelected);
if(totalSelected > 0)
extendedAttributeModel.isOptionsSelected(true);
else
{
extendedAttributeModel.isOptionsSelected(false);
extendedAttributeModel.ISATTRIBUTESELECTED(false);
}
};
//extendedAttributeModel.getOnlySelectedCount = function(modelObject) {
// var totalSelected = 0;
// var selectedAttributeID = modelObject.ATTRIBUTE_ID();
// ko.utils.arrayForEach(modelObject.filterListOptions(), function(item) {
// if (item.isSelected()) {
// totalSelected++;
// }
// });
// return totalSelected;
//};
extendedAttributeModel.onAttributeUncheckCallback = function(modelObject) {
if(modelObject.ATTRIBUTE_CODE().toLowerCase() == "section")
{
ko.utils.arrayForEach(modelObject.filterddlSectionListOptions(), function(outeritem) {
sltModel.setSectionAttributeOriginalStateName(outeritem,-1);
if(outeritem.sectionList().length > 0)
{
ko.utils.arrayForEach(outeritem.sectionList(),function(item){
item.isSectionSelected(false);
});
}
});
}
else
{
ko.utils.arrayForEach(extendedAttributeModel.filterListOptions(), function(item) {
item.isSelected(false);
if(item.childObjects()!=null){
ko.utils.arrayForEach(item.childObjects(), function(item) {
item.isSelected(false);
});
}
});
}
extendedAttributeModel.OptionsCountLabel("");
extendedAttributeModel.OptionsSelectedCount(0);
extendedAttributeModel.isOptionsSelected(false);
};
return extendedAttributeModel;
}
};
};
In short,it replicates the drag one option.Kindly tell me what I am missing??
It looks like you're making use of the knockout-sortable library. I've had problems in the past where the templates had whitespace outside of any elements - it's noted on the project's page also:
Note2: When using named templates, you will have the best results across browsers, if you ensure that there is only a single top-level node inside your template with no surrounding text nodes. Inside of the top-level nodes, you can freely use whitespace/text nodes. So, you will want:
<!-- good - no text nodes surrounding template root node -->
<script id="goodTmpl" type="text/html"><li data-bind="text: name">
<span data-bind="text: name"></span>
</li></script>
<!-- bad -->
<script id="badTmpl" type="text/html">
<li>
<span data-bind="text: name"></span>
</li>
</script>
Related
class Cell {
constructor(game, index) {
this.isEmpty = false;
this.game = game;
this.index = index;
this.height = (game.height / game.dimension);
this.width = (game.width / game.dimension);
this.id = "cell-" + index;
this.cell = this.createCell();
$("#content").append(this.cell);
if (this.index === this.game.dimension * this.game.dimension - 1) {
this.isEmpty = true;
return;
}
this.setImage();
this.setPosition(this.index)
}
createCell() {
const cell = document.createElement("div");
$(cell).css({
"width": this.height + "px",
"height": this.width + "px",
"position": "absolute",
"border": "1px solid #fff"
})
$(cell).attr("id", this.id);
//On click Move to the Empty Space
$(cell).on("click", () => {
if (this.game.gameStarted) {
let validCells = this.game.checkValidCells();
let compareCells = [];
if (validCells.right) {
compareCells.push(this.game.cells[validCells.right].cell);
}
if (validCells.left || validCells.left === 0) {
compareCells.push(this.game.cells[validCells.left].cell);
}
if (validCells.top || validCells.top === 0) {
compareCells.push(this.game.cells[validCells.top].cell);
}
if (validCells.bottom) {
compareCells.push(this.game.cells[validCells.bottom].cell);
}
let i = this.game.cells.findIndex(item => item.cell === cell);
let j = this.game.findEmptyCell();
if (compareCells.indexOf(cell) != -1) {
[this.game.cells[i], this.game.cells[j]] = [this.game.cells[j], this.game.cells[i]];
this.game.cells[i].setPosition(i);
this.game.cells[j].setPosition(j);
if (this.game.checkWin()) {
alert("you won the game!!");
this.game.numberOfMoves = 0;
this.game.gameStarted = false;
}
this.game.numberOfMoves++;
$("#moves").html("Moves: " + this.game.numberOfMoves);
}
this.game.dragTheTile();
}
})
return cell;
}
setImage() {
const left = this.width * (this.index % this.game.dimension);
const top = this.height * Math.floor(this.index / this.game.dimension);
const bgPosition = -left + "px" + " " + -top + "px";
const bgSize = this.game.width + "px " + this.game.height + "px"
$(this.cell).css({
"background": 'url(' + this.game.imageSrc + ')',
"background-position" : bgPosition,
"background-size": bgSize
})
}
setPosition(index) {
const {left, top} = this.getPosition(index);
$(this.cell).css({
"left": left + "px",
"top": top + "px"
})
}
makeDraggable(index) {
let emptyCellIndex = this.game.findEmptyCell();
$(this.cell).draggable({
containment: "parent",
snap: this.game.cells[emptyCellIndex],
cursor: "move",
snapMode: "inner",
snapTolerance: 20,
helper: "clone",
opacity: 0.5
})
}
makeDroppable(index) {
$(this.cell).droppable({
drop: (event, ui) => {
let draggedCell;
draggedCell = ui.draggable;
let i = this.game.cells.findIndex(item => item.cell === draggedCell[0]);
let j = this.game.findEmptyCell();
[this.game.cells[i], this.game.cells[j]] = [this.game.cells[j], this.game.cells[i]];
this.game.cells[i].setPosition(i);
this.game.cells[j].setPosition(j);
this.game.clearDrag();
this.game.numberOfMoves++;
$("#moves").html("Moves: " + this.game.numberOfMoves);
if (this.game.checkWin()) {
alert("you won the game!!");
this.game.numberOfMoves = 0;
this.game.gameStarted = false;
} else {
this.game.dragTheTile();
}
}
})
}
getPosition(index) {
return {
left: this.width * (index % this.game.dimension),
top: this.height * Math.floor(index / this.game.dimension)
}
}
}
class GameBoard {
constructor(dimension){
this.dimension = dimension;
this.imageSrc = 'https://i.ibb.co/1XfXq6S/image.jpg'
this.cells = [];
let length = Math.min(window.innerHeight, window.innerWidth);
this.width = length - 100;
this.height = length - 100;
this.setup();
this.gameStarted = false;
this.numberOfMoves = 0;
}
setup() {
for(let i = 0;i < this.dimension * this.dimension; i++) {
this.cells.push(new Cell(this, i));
}
}
shuffle() {
for (let i = this.cells.length -1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[this.cells[i], this.cells[j]] = [this.cells[j], this.cells[i]];
this.cells[i].setPosition(i);
this.cells[j].setPosition(j);
}
}
findEmptyCell() {
return this.cells.findIndex(cell => cell.isEmpty)
}
checkValidCells() {
const emptyCell = this.findEmptyCell(),
topCell = emptyCell - this.dimension,
leftCell = emptyCell - 1,
rightCell = emptyCell + 1,
bottomCell = emptyCell + this.dimension;
const mod = emptyCell % this.dimension;
let left, right, top, bottom;
if (mod != 0) {
left = leftCell;
}
if (mod != this.dimension -1) {
right = rightCell;
}
if (emptyCell >= this.dimension) {
top = topCell;
}
if (emptyCell < this.dimension * (this.dimension - 1)) {
bottom = bottomCell
}
return {right: right, left: left, top: top, bottom: bottom};
}
findPosition(index) {
return this.cells.findIndex(cell => cell.index === index);
}
checkWin() {
for (let i = 0; i < this.cells.length; i++) {
if (i != this.cells[i].index) {
return false;
}
}
return true;
}
clearDrag() {
this.cells.forEach(cell => {
if($(cell.cell).data('ui-draggable')) $(cell.cell).draggable("destroy");
})
}
dragTheTile() {
this.clearDrag();
const validCells = this.checkValidCells();
let availableCells = [];
if (validCells.right) {
availableCells.push(this.cells[this.findPosition(validCells.right)].index);
}
if (validCells.left || validCells.left === 0) {
availableCells.push(this.cells[this.findPosition(validCells.left)].index);
}
if (validCells.top || validCells.top === 0) {
availableCells.push(this.cells[this.findPosition(validCells.top)].index);
}
if (validCells.bottom) {
availableCells.push(this.cells[this.findPosition(validCells.bottom)].index);
}
let emptyCellIndex = this.findEmptyCell();
availableCells.forEach(cell => {
this.cells[cell].makeDraggable(cell);
this.cells[emptyCellIndex].makeDroppable(cell);
})
}
solve() {
let i;
for (i = 0; i < this.cells.length; i++) {
let j = this.cells[i].index;
if (i != j) {
[this.cells[i], this.cells[j]] = [this.cells[j], this.cells[i]];
this.cells[i].setPosition(i);
this.cells[j].setPosition(j);
i--;
}
if (i === this.cells.length - 1) {
[this.cells[i], this.cells[i - 1]] = [this.cells[i - 1], this.cells[i]];
this.cells[i].setPosition(i);
this.cells[i - 1].setPosition(i - 1);
}
}
}
}
$(document).ready(() => {
let gb;
$("#startGame").on("click", () => {
gb.gameStarted = true;
gb.shuffle();
gb.numberOfMoves = 0;
$("#moves").html("Moves: " + gb.numberOfMoves);
gb.clearDrag();
gb.dragTheTile();
})
$("#solve").on("click", () => {
if (gb.gameStarted) {
gb.solve();
gb.clearDrag();
gb.dragTheTile();
}
})
$("#generate-puzzle").on("click", () => {
let number = parseInt($("#dimension").val());
if(number >= 3 && Number.isInteger(number)) {
gb = new GameBoard(number);
$("#content").css("display", "block");
$("#solve").css("display", "block");
$("#startGame").css("display", "block");
$("#enter-dimension").css("display", "none");
$("#content").css({
width: gb.width + "px",
height : gb.height + "px"
})
} else {
$("#alert").css("display", "block");
}
})
})
body {
background-color: #3d3d3d;
}
#content {
width: 400px;
height : 400px;
background-color: #fff;
margin: auto;
position: relative;
display: none;
}
#startGame {
margin: auto;
display: none;
}
#solve {
margin: auto;
display: none;
}
#alert {
display: none;
}
#moves {
margin: auto;
padding: 5px;
text-align: center;
color: #FFFF00;
}
#enter-dimension {
margin: auto;
}
#label-dimension {
color: #ddd;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- JQuery -->
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js" integrity="sha512-uto9mlQzrs59VwILcLiRYeLKPPbS/bT71da/OEBYEwcdNUk8jYIy+D176RYoop1Da+f9mvkYrmj5MCLZWEtQuA==" crossorigin="anonymous"></script>
<title>Image Puzzle</title>
</head>
<body>
<div class="container col-md-6">
<div id="moves">
Moves : 0
</div>
<div class="d-flex justify-content-center row">
<div class="col-md-6">
<div class="mt-5" id="enter-dimension">
<form>
<div class="form-group">
<label id="label-dimension" for="dimension">Enter the Dimension of the puzzle</label>
<input type="number" class="form-control" id="dimension">
</div>
<div class="alert alert-danger" id="alert" role="alert">
Enter a Valid Positive Integer Greater than or Equal to 3
</div>
<button type="button" class="btn btn-primary" id="generate-puzzle">Generate the Puzzle</button>
</form>
</div>
</div>
</div>
<div class="row">
<!--The GRid Layout and the tiles -->
<div class="mt-3" id="content">
</div>
</div>
<!--Button to Start the Game -->
<div class="row buttons">
<button type="button" class="btn btn-info mt-2" id="startGame">Start Game</button>
<button type="button" class="btn btn-success mt-2" id="solve">Solve This!</button>
</div>
</div>
</body>
</html>
The puzzle works perfectly but with one issue. If I drag-drop a cell to an empty cell I am unable to drag and drop that tile again to its previous position which is the current empty cell. The way I see it there is a problem somewhere in makeDraggable() and dragTheTile() functions.
When I move a cell with a mouse click I can drag and drop that same cell to its previous position (which is the current empty cell) or If I drag and drop a cell to an empty space I can move it to its previous position (which is the current empty cell) with mouse click. But after dragging the same cell cannot be dragged again. looks like draggable() is disabled for that particular cell.
Once a cell is dragged, The next move it should be a draggable cell (since it is a neighbour cell to the empty cell) When I make it draggable the "ui-draggable" class is added to it but "ui-draggable-handle" class is missing.
The problem was with the function where I cleared the draggable property:
clearDrag() {
this.cells.forEach(cell => {
if($(cell.cell).data('ui-draggable')) $(cell.cell).draggable("destroy");
})
}
The "destroy" method gives some conflicts.
The alternative method I found was to first initialize all the cells with draggable property but disable it in the beginning and enabling it again when I need.
the property used here was "disabled: true" and "disabled: false".
Instead of the dragTheTile() function I wrote a new function called dragTile() as follows:
dragTile() {
this.cells.forEach(cell => {
cell.makeDraggable();
})
const validCells = this.checkValidCells();
let availableCells = [];
if (validCells.right) {
availableCells.push(this.cells[this.findPosition(validCells.right)].index);
}
if (validCells.left || validCells.left === 0) {
availableCells.push(this.cells[this.findPosition(validCells.left)].index);
}
if (validCells.top || validCells.top === 0) {
availableCells.push(this.cells[this.findPosition(validCells.top)].index);
}
if (validCells.bottom) {
availableCells.push(this.cells[this.findPosition(validCells.bottom)].index);
}
let emptyCellIndex = this.findEmptyCell();
availableCells.forEach(cell => {
$(this.cells[cell].cell).draggable({disabled: false});
})
this.cells[emptyCellIndex].makeDroppable(availableCells);
}
This solved the problem very easily.
I'm trying to highlight text in realtime using JQuery or Javascript. Current code doesn't want to search from .filters-no-actions block.
How it should look like:
Note: I don't want to add new text to the .filters-no-actions block, but only find text in this block using input value and highlight them.
CSS
.highlight_y {
background-color: yellow;
}
p, div {
display: inline-block;
}
HTML
<input id="ue__searchInput" placeholder="Search">
<div class="filters-no-actions">
<p>Example text to search</p>
<p>Lorem ipsum and...</p>
</div>
Script
$(document).ready(function(){
var search_input = $('#ue__searchInput');
$(search_input).change(function(){
var search_box = $('.filters-no-actions').text();
var search_box_val = search_box.val();
if (search_box_val == this.val()) {
search_box.append('<div class="highlight_y">' + search_box_val + '</div>');
}
});
});
You need to use the keyup event instead of the change event. Here is how you can do it:
$(document).ready(function(){
var search_input = $('#ue__searchInput');
$(document).on('keyup', '#ue__searchInput',function(){
var search_box = $('.filters-no-actions').text();
var search_box_val = search_input.val();
if (search_input.val() != '') {
$('.highlight_y').html('<p class="highlight_text">' + search_input.val() + '</p>');
}
else
{
$('.highlight_y').html('');
}
});
});
.highlight_y {
background-color: yellow;
}
p, div {
display: inline-block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
</head>
<body>
<input id="ue__searchInput" placeholder="Search">
<div class="highlight_y"></div>
<div class="filters-no-actions">
<p>Example text to search</p>
<p>Lorem ipsum and...</p>
</div>
</body>
</html>
As Santu Roy said (+1 for him) you could try a jQuery highlight plugin like this: http://bartaz.github.io/sandbox.js/jquery.highlight.html.
In this exemple, I used it in a small delay function that is trigger after 100ms (you can change this time) your last keyup [thanks to Ata ul Mustafa for this solution: How to trigger an event in input text after I stop typing/writing? +1 also for him].
Try it:
/* This is the plugin. Put it in an external file
=> https://github.com/bartaz/sandbox.js/blob/master/jquery.highlight.js
Here you can find all the documentation: http://bartaz.github.io/sandbox.js/jquery.highlight.html
*/
jQuery.extend({
highlight: function (node, re, nodeName, className) {
if (node.nodeType === 3) {
var match = node.data.match(re);
if (match) {
var highlight = document.createElement(nodeName || 'span');
highlight.className = className || 'highlight';
var wordNode = node.splitText(match.index);
wordNode.splitText(match[0].length);
var wordClone = wordNode.cloneNode(true);
highlight.appendChild(wordClone);
wordNode.parentNode.replaceChild(highlight, wordNode);
return 1; //skip added node in parent
}
} else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
!/(script|style)/i.test(node.tagName) && // ignore script and style nodes
!(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
for (var i = 0; i < node.childNodes.length; i++) {
i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
}
}
return 0;
}
});
jQuery.fn.unhighlight = function (options) {
var settings = { className: 'highlight', element: 'span' };
jQuery.extend(settings, options);
return this.find(settings.element + "." + settings.className).each(function () {
var parent = this.parentNode;
parent.replaceChild(this.firstChild, this);
parent.normalize();
}).end();
};
jQuery.fn.highlight = function (words, options) {
var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false };
jQuery.extend(settings, options);
if (words.constructor === String) {
words = [words];
}
words = jQuery.grep(words, function(word, i){
return word != '';
});
words = jQuery.map(words, function(word, i) {
return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
});
if (words.length == 0) { return this; };
var flag = settings.caseSensitive ? "" : "i";
var pattern = "(" + words.join("|") + ")";
if (settings.wordsOnly) {
pattern = "\\b" + pattern + "\\b";
}
var re = new RegExp(pattern, flag);
return this.each(function () {
jQuery.highlight(this, re, settings.element, settings.className);
});
};
$(document).ready(function(){
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
var search_input = $('#ue__searchInput');
$(search_input).on('keyup',function(){
delay(function(){
$(".filters-no-actions").unhighlight({ className: 'highlight_y'}); /*remove all highlight before create a new one */
$(".filters-no-actions").highlight($(search_input).val(), { className: 'highlight_y'});
}, 100 );
});
});
.highlight_y {
background-color: #FFFF88;
}
p, div {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="ue__searchInput" placeholder="Search">
<div class="highlight_y"></div>
<div class="filters-no-actions">
<p>Example text to search</p>
<p>Lorem ipsum and...</p>
</div>
I'm using fancybox to display image gallery.
I have this layout:
<div class="avatar">
<a class="avatar-item" data-fancybox="group" data-caption="Caption #1" href="img/avatars/jessica1.jpeg">
<img src="./img/avatars/jessica1.jpeg" width="145" height="145" alt="">
</a>
<a class="avatar-item" data-fancybox="group" data-caption="Caption #2" href="img/avatars/jessica2.jpeg">
<img src="./img/avatars/jessica2.jpeg" alt="">
</a>
</div>
And when I click on preview - gallery popup occurs, but it shows 4 images instead of 2. I included fancybox via data-attributes, without javascript. Tried magnific popup with gallery option - got same result.
link href attribute value and internal image src attribute are the same.
I don't have a thumbnail, display image cropped with css.
Hee is CSS:
.avatar {
&.slick-dotted.slick-slider {
margin-bottom: 0;
}
a.avatar-item {
width: 146px;
height: 146px;
display: inline-block;
}
width: 146px;
height: 146px;
border: 4px solid #FFF;
float: left;
position: relative;
overflow: hidden;
img {
height: 100%;
}
}
Found the problem. I use slick-slider before with infinite: true parameter, which creates an extra slides, so I got count slides x2
I have extended the _run function. Now duplicate images are removed from the gallery and still the correct key is returned. Furthermore I have the possibility to arrange the images in any order via data-facybox position.
So it is also possible to use the infinity version of the slick slider.
function _run(e, opts) {
var tempItems,
items = [],
newItem,
reOrder,
duplicates = false,
pos,
index = 0,
$target,
value,
instance;
// Avoid opening multiple times
if (e && e.isDefaultPrevented()) {
return;
}
e.preventDefault();
opts = opts || {};
if (e && e.data) {
opts = mergeOpts(e.data.options, opts);
}
$target = opts.$target || $(e.currentTarget).trigger("blur");
instance = $.fancybox.getInstance();
if (instance && instance.$trigger && instance.$trigger.is($target)) {
return;
}
if (opts.selector) {
tempItems = $(opts.selector);
} else {
// Get all related items and find index for clicked one
value = $target.attr("data-fancybox") || "";
if (value) {
tempItems = e.data ? e.data.items : [];
tempItems = tempItems.length ? tempItems.filter('[data-fancybox="' +
value + '"]') : $('[data-fancybox="' + value + '"]');
} else {
tempItems = [$target];
}
}
if (tempItems.length > 1) {
$.each(tempItems, function(key, item) { // prevents duplicate images in the gallery
newItem = false;
if (typeof item !== 'undefined') {
if (items.length > 0) {
var found = items.filter(function (items) { return items.href ==
item.href });
if (found.length == 0) {
newItem = true;
} else {
duplicates = true;
}
} else {
newItem = true;
}
}
if (newItem) {
// Sort if the attribute data-fancybox-pos exists
if ($(item).data('fancybox-pos')) {
reOrder = true;
pos = $(item).data('fancybox-pos') - 1;
if (pos in items) {
tempItem = items[pos];
items[pos] = item;
items.push(tempItem);
} else {
items[pos] = item;
}
} else {
items.push(item);
}
}
});
} else {
items = tempItems;
}
if (duplicates || reOrder) {
// find correct index if there were duplicates
$.each(items, function(key, item) {
if (item.href == $target[0].href) {
index = key;
}
});
} else {
index = $(items).index($target);
}
// Sometimes current item can not be found
if (index < 0) {
index = 0;
}
instance = $.fancybox.open(items, opts, index);
// Save last active element
instance.$trigger = $target;
}
I'm making a system that allows reorder of products. Each display area in a shop is a unit, which has several groups which (in turn) house several products. I am currently using knockout to load a unit via a select element, then cycling through the groups providing quantity fields for products.
This all works, but I need a select all button which sets all products in a group to have a quantity of 1. I have given the group object a function (allForOne()) which should cycle through each product in the group's observable array and set this to one, however, no matter how I bind this to the #allInOne button, it always applies this to the last group in the unit's observable group array.
For example, when the code first loads, I want to select all in the first group and make their quantity one, but it only changes the quantity in the last group.
I use the same selector in the same binding to "alert" the correct unit name (based on the unit's groupCounter property) before I call the function, but it returns two different values.
Why is it not affecting the current group? I can't work this out for the life of me and Google hasn't been much help.
Here is a JSFiddle, or you can see the code below:
Here is the view:
<div class="select-container">
<label for="unit" class="label">Unit</select>
<select name="unit" class="select-unit drop-shadow" data-bind='options: units, optionsText: "skuName", optionsCaption: "Select...", value: unit'></select>
<div>
</div>
<div class="unit-image" data-bind="with: unit">
<img data-bind="attr{ src: $parent.unit().image, alt: $parent.unit().name }">
</div>
<div data-bind="with: unit" class="clearfix products-container">
<div class="clearfix" data-bind="with: $parent.groups()[$parent.groupCounter()]">
<!--<div data-bind="style: { width: (((parseInt(limit) / parseInt($root.totalLimit)) * 100) - 1)+'%', marginRight: '0.5%', marginLeft: '0.5%'}" style="display: block; position: relative; float: left;">-->
<h2 data-bind="text: name" style="margin-bottom: 12px;"></h2>
<div data-bind="foreach: {data: products/*, beforeRemove: hideProduct, afterRender: showProduct*/}">
<div class="prod-page-product drop-shadow" data-bind="style: { width: ((100 / $parent.limit) - 1)+'%', marginRight: '0.5%', marginLeft: '0.5%'}">
<p><span data-bind='text: sku'></span></p>
<div>
<div class="add-container">+</div>
<div><input type="number" class="is-numeric" min="0" data-bind='value: quantity, valueUpdate: "afterkeydown"' /></div>
<div class="remove-container">-</div>
</div>
</div>
</div>
<!--</div>-->
<div class="clearfix">
<button id="nextProdGroup" class="products-button float-left" data-bind="enable:$root.firstGroupBool, click: $root.prevGroup, style: { width: productWidth, maxWidth: ((100/4) - 1)+'%', marginRight: '0.5%', marginLeft: '0.5%'}">Prev Group</button>
<button id="prevProdGroup" class="products-button float-left" data-bind="enable:$root.nextGroupBool, click: $root.nextGroup, style: { width: productWidth, maxWidth :((100/4) - 1)+'%', marginRight: '0.5%', marginLeft: '0.5%'}">Next Group</button>
<!--This is the offending button binding-->
<button id="allForOne" class="products-button float-left" data-bind="click: function() { alert('Expected Group: '+$root.groups()[$root.groupCounter()].name()); $root.groups()[$root.groupCounter()].allForOne()}, style: { width: productWidth, maxWidth :((100/4) - 1)+'%', marginRight: '0.5%', marginLeft: '0.5%'}">This is what messes up</button>
</div>
</div>
</div>
<div id="shadow-overlay" class="ui-overlay" data-bind="visible: loaderBool">
<div class="ui-widget-overlay"></div>
<div id="top-overlay" class="ui-overlay" style="width: 50%; height: 80%; position: absolute; left: 25%; top: 10%;"></div>
<div id="ajax-loading-container">
<p class="ajax-loader-text">Loading...</p>
</div>
Here is the viewModel:
var Product = function(id, sku) {
var self = this;
//Properties
self.id = ko.observable(id);
self.sku = ko.observable(sku);
self.quantity = ko.observable(0);
//Methods
self.incrementQuantity = function(product) {
var previousQuantity = parseInt(self.quantity());
self.quantity(previousQuantity+1);
};
self.decrementQuantity = function(product) {
var previousQuantity = parseInt(self.quantity());
if(self.quantity() > 0)
{
self.quantity(previousQuantity-1);
}
};
};
//The object with the called function
var Group = function(name, limit, productList)
{
self = this;
//Properties
self.name = ko.observable(name);
self.nametwo = name;
self.limit = limit;
self.products = ko.observableArray();
self.productWidth = ko.pureComputed(function(a, b)
{
return ((100 / limit) - 1)+'%';
});
//Methods
//---------The offending function
self.allForOne = function() {
alert("Returned Group: "+self.name());
ko.utils.arrayForEach(self.products(), function(product) {
product.quantity(1);
});
};
//Initial population
$.each(productList, function(key, product) {
self.products.push(new Product(product.id, product.sku));
});
}
var Unit = function() {
var self = this;
//Properties
self.unit = ko.observable();
self.groups = ko.observableArray();
self.groupCounter = ko.observable(0);
self.lastGroup = ko.observable(true);
self.totalLimit = 0;
self.saved = true;
self.loaderBool = ko.observable(false);
self.firstGroupBool = ko.pureComputed(function()
{
return (self.groupCounter() < 1 ? false : true);
});
self.nextGroupBool = ko.pureComputed(function()
{
return (self.lastGroup() == true ? false : true);
});
self.unit.subscribe(function() {
self.populateGroup();
});
//Methods
self.onLoadCheck = (function() {
if(units.length == 1)
{
self.unit(units[0]);
}
});
self.populateGroup = function() {
self.loaderBool(true);
self.groups([]);
//setTimeout(function() {
self.TotalLimit = 0;
$.each(self.unit().group, function(groupKey, groupVal) {
//setTimeout(function() {
self.groups.push(new Group(groupVal.name, groupVal.limit, groupVal.product));
//}, 0);
self.totalLimit += parseInt(groupVal.limit);
});
self.groupCounter(0);
self.lastGroup(false);
self.loaderBool(false);
//}, 0);
console.log(self.groups());
console.log(self.groups()[self.groupCounter()]);
};
self.prevGroup = function(a, b) {
self.save(a, b);
var groupCounter = parseInt(self.groupCounter());
self.groupCounter(groupCounter-1);
if(self.groupCounter() != self.groups().length-1)
{
self.lastGroup(false);
}
};
self.nextGroup = function(a, b) {
self.save(a, b);
var groupCounter = parseInt(self.groupCounter());
self.groupCounter(groupCounter+1);
if(self.groupCounter() == self.groups().length-1)
{
self.lastGroup(true);
}
};
self.save = function(a, b) {
var productsToSave = self.groups()[self.groupCounter()].products();
var dataToSave = $.map(productsToSave, function(line) {
return {
id: line.id(),
quantity: line.quantity()
}
});
if(productsToSave.length == 0)
{
dialog("Error", "You cannot submit before you add products.");
return false;
}
else
{
var caller = $(b.toElement);
//sendOrder("baskets/update", {products: dataToSave}, caller);
if(caller.attr("id") == "submitProdGroup")
{
window.location = baseUrl+"basket";
}
}
};
};
Take a look at this: http://jsfiddle.net/rks5te7v/5/ I rewrote your code using a viewmodel like this (look at the url above):
function ViewModel() {
var self = this;
self.units = ko.observableArray();
self.chosenUnit = ko.observable();
self.chosenGroup = ko.observable(0);
self.goToNextGroup = function () {
self.chosenGroup(parseInt(self.chosenGroup()) + 1);
};
self.goToPrevGroup = function () {
self.chosenGroup(parseInt(self.chosenGroup()) - 1);
};
self.setGroupAs1 = function () {
var products = self.chosenUnit().groups()[self.chosenGroup()].products();
ko.utils.arrayForEach(products, function (product) {
product.quantity(1);
});
};
//loading data
var product1 = new Product('1', 'P1');
var product2 = new Product('2', 'P2');
var product3 = new Product('3', 'P3');
var product4 = new Product('4', 'P4');
var group1 = new Group('G1', [product1, product2]);
var group2 = new Group('G2', [product3, product4]);
self.units.push(new Unit('Unit 1', [group1, group2]));
};
Does it solve your problem? :)
I have the following code. When I press the down/up key, i can successfully select the different value but the focus is still on the main input box. I want to move focus to the selected element. That is if i press down key and if selected element is John Doe, then the focus should also be on John doe element and not on the input box.
I set the following 2 things in the code but they are not working
$selected.focus();
and
$current.focus();
What am I doing wrong ?
You can't just focus() a <li> element. From the docs:
The focus event is sent to an element when it gains focus. This event is implicitly applicable to a limited set of elements, such as form elements (<input>, <select>, etc.) and links (<a href>). In recent browser versions, the event can be extended to include all element types by explicitly setting the element's tabindex property. An element can gain focus via keyboard commands, such as the Tab key, or by mouse clicks on the element.
You can try setting the tabindex property.
LIVE DEMO
I have added a counter variable , and if counter > 1 then only key Up, Down, Enter method calls, also on Enter Hit again i am setting counter=0 so you get last selected Li. Instead of calling removeClass('selected') everytime, i am calling it only if my counter >1.
$(document).ready(function () {
var $listItems = $('li.autocomplete-list'),
$div = $('div.autofill'),
$input = $('#example');
$div.hide();
var allLI = $(".autocomplete .autocomplete-list");
var counter = 0;
$('input#example').on('keydown', function (e) {
var key = e.keyCode,
$selected = $listItems.filter('.selected'),
$current;
//console.log("Show");
$div.show();
counter++;
if (counter > 1) {
if (key == 40) { // Down key
$selected.focus();
if (!$selected.length || $selected.is(':last-child')) {
$current = $listItems.eq(0);
} else {
$current = $selected.next();
}
} else if (key == 38) { // Up key
if (!$selected.length || $selected.is(':first-child')) {
$current = $listItems.last();
} else {
$current = $selected.prev();
}
} else if (key == 13) {
var value = $selected.text().split('(')[0].trim();
$input.val(value);
$div.hide();
counter = 0;
}
if ($current) {
allLI.removeClass('selected');
$current.addClass('selected');
$current.focus();
}
}
});
$('li.autocomplete-list').on('click', function (e) {
var value = $(this).text().split('(')[0].trim();
$input.val(value);
$div.hide();
});
$('li.autocomplete-list').hover(
function () {
$(this).addClass('partial_selected')
},
function () {
$(this).removeClass('partial_selected')
});
});
You need to explicitly assign tabindex property for the list items if you want to focus on them. see http://jsfiddle.net/tkay/40ctx17s/1/ . But a downside to this is you will not be able to move further down in the list since input is not focused.
$(document).ready(function () {
var $listItems = $('li.autocomplete-list'),
$div = $('div.autofill'),
$input = $('#example');
$div.hide();
$('input#example').on('keydown', function (e) {
var key = e.keyCode,
$selected = $listItems.filter('.selected'),
$current;
$div.show();
$listItems.removeClass('selected');
if (key == 40) { // Down key
$selected.focus();
if (!$selected.length || $selected.is(':last-child')) {
$current = $listItems.eq(0);
} else {
$current = $selected.next();
}
} else if (key == 38) { // Up key
if (!$selected.length || $selected.is(':first-child')) {
$current = $listItems.last();
} else {
$current = $selected.prev();
}
} else if (key == 13) {
var value = $selected.text().split('(')[0].trim();
$input.val(value) ;
$div.hide();
}
if ($current) {
$current.addClass('selected');
$current.focus();
console.log($current);
}
});
$('li.autocomplete-list').on('click', function (e) {
var value = $(this).text().split('(')[0].trim();
$input.val(value);
$div.hide();
});
$('li.autocomplete-list').hover(
function(){ $(this).addClass('partial_selected') },
function(){ $(this).removeClass('partial_selected') }
);
});
.selected {
background: #a4a4a4;
}
.hover {
background: #A9D0F5;
}
ul {
padding: 5px 0;
}
li {
padding: 6px 3px;
}
.autofill {
width: 250px;
}
.input {
width: 250px;
height: 2.2em;
padding: .3em .5em;
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<input type="text" class="input" name="example" id="example" placeholder="example..." autocomplete="off" list="examplelist" />
<div class="autofill">
<ul class="autocomplete">
<li class="autocomplete-list" tabindex="-1">John Doe (San Jose, CA)</li>
<li class="autocomplete-list" tabindex="-1">Jane Doe (San Francisco, CA)</li>
<li class="autocomplete-list" tabindex="-1">John Jane (San Carlos, CA)</li>
</ul>
</div>
Updated Answer after comments of OP
$(document).ready(function () {
var $listItems = $('li.autocomplete-list'),
$div = $('div.autofill'),
$input = $('#example');
$div.hide();
$('input#example').on('keydown', function (e) {
var key = e.keyCode,
$selected = $listItems.filter('.selected'),
$current;
$div.show();
$listItems.removeClass('selected');
if (key == 40) { // Down key
$selected.focus();
if (!$selected.length || $selected.is(':last-child')) {
$current = $listItems.eq(0);
} else {
$current = $selected.next();
}
} else if (key == 38) { // Up key
if (!$selected.length || $selected.is(':first-child')) {
$current = $listItems.last();
} else {
$current = $selected.prev();
}
} else if (key == 13) {
if ($selected.length){
e.preventDefault();
}
var value = $selected.text().split('(')[0].trim();
$input.val(value) ;
$div.hide();
}
if ($current) {
$current.addClass('selected');
}
});
$('li.autocomplete-list').on('click', function (e) {
var value = $(this).text().split('(')[0].trim();
$input.val(value);
$div.hide();
});
$('li.autocomplete-list').hover(
function(){ $(this).addClass('partial_selected') },
function(){ $(this).removeClass('partial_selected') }
);
$("#frm").submit(function(){
console.log('submitted');
});
});
.selected {
background: #a4a4a4;
}
.hover {
background: #A9D0F5;
}
ul {
padding: 5px 0;
}
li {
padding: 6px 3px;
}
.autofill {
width: 250px;
}
.input {
width: 250px;
height: 2.2em;
padding: .3em .5em;
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<form id="frm">
<input type="text" class="input" name="example" id="example" placeholder="example..." autocomplete="off" list="examplelist" />
<input type="submit" value="Submit"/>
</form>
<div class="autofill">
<ul class="autocomplete">
<li class="autocomplete-list">John Doe (San Jose, CA)</li>
<li class="autocomplete-list">Jane Doe (San Francisco, CA)</li>
<li class="autocomplete-list">John Jane (San Carlos, CA)</li>
</ul>
</div>