JQuery scroll listbox imitation with keyboard navigation? - javascript

My javascript listbox implementation:
$(function() {
$('#listbox1').on('keydown', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
var $e = $(this).find('.selected');
switch(e.which) {
case 38:
($e.prev().length) && ($e = $e.removeClass('selected').prev().addClass('selected'));
$(this).scrollTop( $e.position().top + $(this).scrollTop() );
break;
case 40:
($e.next().length) && ($e = $e.removeClass('selected').next().addClass('selected'));
$(this).scrollTop( $e.position().top + $(this).scrollTop() );
break;
}
});
});
Here is a fiddle: http://jsfiddle.net/7qy8p2x1/.
There are presented a real listbox and his imitation. I do not go so they scrolled the same using keyboard navigation arrows (up & down). A real listbox is not scrolling until it reaches the last element but imitation does so (following each element). ScrollUp is working properly.
Please, help me implement scrollDown behaviour correctly.

i use .jspPane for scrolling list, but u can it without jsp (if u ok with coffe) can show to you .coffee - it more readable. If u have question - ill help u
var keyboard_control, scroll_to_active;
keyboard_control = function() {
$('.control-edu').on('keydown', 'input', function(e) {
var $active, $first, $last, $this, i, temp;
$this = $(this);
$active = $('.jspPane span.active');
$first = $('.jspPane span.opinion').first();
$last = $('.jspPane span.opinion').last();
if ($this.closest('.control-edu').find('.options span').length > 1) {
if (e.keyCode === 38) {
if ($active.length > 0) {
if ($active.prev().length > 0) {
temp = $active.prev();
$active.removeClass('active');
temp.addClass('active');
} else {
$active.removeClass('active');
$last.addClass('active');
}
} else {
$last.addClass('active');
}
i = $('.jspPane span.active').closest('.control-edu').index();
scroll_to_active(i, 0);
} else if (e.keyCode === 40) {
if ($active.length > 0) {
if ($active.next().length > 0) {
temp = $active.next();
$active.removeClass('active');
temp.addClass('active');
} else {
$active.removeClass('active');
$first.addClass('active');
}
} else {
$first.addClass('active');
}
i = $('.jspPane span.active').closest('.control-edu').index();
scroll_to_active(i, 1);
} else if (e.keyCode === 13 && $active.length > 0) {
$active.click();
}
}
});
};
scroll_to_active = function(i, flag) {
var $active;
$active = $('.jspPane span.active');
if (flag === 1) {
if (($active.next().length > 0) && ($active.prev().length > 0)) {
api[i - 1].scrollToElement($active.next());
} else if ($active.prev().length === 0) {
api[i - 1].scrollToElement($active);
} else if ($active.next().length === 0) {
api[i - 1].scrollToBottom();
}
} else if (flag === 0) {
if ($active.next().length > 0) {
api[i - 1].scrollToElement($active);
} else {
api[i - 1].scrollToBottom();
}
} else {
return
}
};
Edited: remove js2coffee return

Check this out
http://jsfiddle.net/7qy8p2x1/1/
$(function() {
$('#listbox1').on('keydown', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
var $e = $(this).find('.selected');
switch(e.which) {
case 38:
($e.prev().length) && ($e = $e.removeClass('selected').prev().addClass('selected'));
$(this).scrollTop( $e.position().top + $(this).scrollTop() );
break;
case 40:
($e.next().length) && ($e = $e.removeClass('selected').next().addClass('selected'));
$(this).scrollTop( $e.position().top + $(this).scrollTop() );
break;
}
});
$('#listbox2').on('keydown', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
var $e = $(this).find(':selected');
switch(e.which) {
case 38:
($e.prev().length) && ($e = $e.removeClass('selected').prev().addClass('selected'));
$(this).scrollTop( $e.position().top + $(this).scrollTop() );
break;
case 40:
($e.next().length) && ($e = $e.removeClass('selected').next().addClass('selected'));
$(this).scrollTop( $e.position().top + $(this).scrollTop() );
break;
}
}); });

My solution is:
$(function() {
$('#listbox1').on('keydown', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
var $e = $(this).find('.selected');
switch(e.which) {
case 38:
($e.prev().length) && ($e = $e.removeClass('selected').prev().addClass('selected'));
($e.position().top < 0) && ($(this).scrollTop( $e[0].offsetTop ));
break;
case 40:
($e.next().length) && ($e = $e.removeClass('selected').next().addClass('selected'));
($e.position().top - $(this).height() + $e.height() > 0) && ($(this).scrollTop( $e[0].offsetTop - $(this).height() + $e.outerHeight() ));
break;
}
});
});
http://jsfiddle.net/7qy8p2x1/7/
Cross-browser (but workaround) solution:
http://jsfiddle.net/7qy8p2x1/4/

Related

When I pressing polish letter such as: ó, ł, ą, ż Script moves 3 spaces, when should move only 1 space

When I pressing polish letter such as: ó, ł, ą, ż Script moves 3 spaces, when should move only 1 space.
At this page you can try this script: http://daniel.jajuwa.pl/krzyzowka/index.html
I found the code on the internet, that's why I'm writing here because here is the best chance for a reply;)
Thanks for help :D
(function($){
$.fn.crossword = function(entryData) {
var puzz = {};
puzz.data = entryData;
this.after('<div id="puzzle-clues"><h2>Poziomo</h2><ol id="across"></ol><h2>Pionowo</h2><ol id="down"></ol></div>');
// initialize some variables
var tbl = ['<table id="puzzle">'],
count = 12,
counter = 0,
puzzEl = this,
clues = $('#puzzle-clues'),
clueLiEls,
coords,
entryCount = puzz.data.length,
entries = [],
rows = [],
cols = [],
solved = [],
tabindex,
$actives,
activePosition = 0,
activeClueIndex = 0,
currOri,
targetInput,
mode = 'interacting',
solvedToggle = false,
z = 0;
var puzInit = {
init: function() {
currOri = 'across';
puzz.data.sort(function(a,b) {
return a.position - b.position;
});
puzzEl.delegate('input', 'keyup', function(e){
mode = 'interacting';
switch(e.which) {
case 39:
case 37:
currOri = 'across';
break;
case 38:
case 40:
currOri = 'down';
break;
default:
break;
}
if ( e.keyCode === 9) {
return false;
} else if (
e.keyCode === 37 ||
e.keyCode === 38 ||
e.keyCode === 39 ||
e.keyCode === 40 ||
e.keyCode === 8 ||
e.keyCode === 46 ) {
if (e.keyCode === 8 || e.keyCode === 46) {
currOri === 'across' ? nav.nextPrevNav(e, 37) : nav.nextPrevNav(e, 38);
} else {
nav.nextPrevNav(e);
}
e.preventDefault();
return false;
} else {
console.log('input keyup: '+solvedToggle);
puzInit.checkAnswer(e);
}
e.preventDefault();
return false;
});
// tab navigation handler setup
puzzEl.delegate('input', 'keydown', function(e) {
if ( e.keyCode === 9) {
mode = "setting ui";
if (solvedToggle) solvedToggle = false;
//puzInit.checkAnswer(e)
nav.updateByEntry(e);
} else {
return true;
}
e.preventDefault();
});
// tab navigation handler setup
puzzEl.delegate('input', 'click', function(e) {
mode = "setting ui";
if (solvedToggle) solvedToggle = false;
console.log('input click: '+solvedToggle);
nav.updateByEntry(e);
e.preventDefault();
});
// click/tab clues 'navigation' handler setup
clues.delegate('li', 'click', function(e) {
mode = 'setting ui';
if (!e.keyCode) {
nav.updateByNav(e);
}
e.preventDefault();
});
// highlight the letter in selected 'light' - better ux than making user highlight letter with second action
puzzEl.delegate('#puzzle', 'click', function(e) {
$(e.target).focus();
$(e.target).select();
});
// DELETE FOR BG
puzInit.calcCoords();
// Puzzle clues added to DOM in calcCoords(), so now immediately put mouse focus on first clue
clueLiEls = $('#puzzle-clues li');
//$('#' + currOri + ' li' ).eq(0).addClass('clues-active').focus();
//REMOVED TO STOP FIRST CLUE FROM LOADING ^^^^^
// DELETE FOR BG
puzInit.buildTable();
puzInit.buildEntries();
},
}; // end puzInit object
var nav = {
nextPrevNav: function(e, override) {
var len = $actives.length,
struck = override ? override : e.which,
el = $(e.target),
p = el.parent(),
ps = el.parents(),
selector;
util.getActivePositionFromClassGroup(el);
util.highlightEntry();
util.highlightClue();
$('.current').removeClass('current');
selector = '.position-' + activePosition + ' input';
//console.log('nextPrevNav activePosition & struck: '+ activePosition + ' '+struck);
// move input focus/select to 'next' input
switch(struck) {
case 39:
p
.next()
.find('input')
.addClass('current')
.select();
break;
case 37:
p
.prev()
.find('input')
.addClass('current')
.select();
break;
case 40:
ps
.next('tr')
.find(selector)
.addClass('current')
.select();
break;
case 38:
ps
.prev('tr')
.find(selector)
.addClass('current')
.select();
break;
default:
break;
}
},
updateByNav: function(e) {
var target;
$('.clues-active').removeClass('clues-active');
$('.active').removeClass('active');
$('.current').removeClass('current');
currIndex = 0;
target = e.target;
activePosition = $(e.target).data('position');
util.highlightEntry();
util.highlightClue();
$('.active').eq(0).focus();
$('.active').eq(0).select();
$('.active').eq(0).addClass('current');
// store orientation for 'smart' auto-selecting next input
currOri = $('.clues-active').parent('ol').prop('id');
activeClueIndex = $(clueLiEls).index(e.target);
//console.log('updateByNav() activeClueIndex: '+activeClueIndex);
},
// Sets activePosition var and adds active class to current entry
updateByEntry: function(e, next) {
var classes, next, clue, e1Ori, e2Ori, e1Cell, e2Cell;
if(e.keyCode === 9 || next){
// handle tabbing through problems, which keys off clues and requires different handling
activeClueIndex = activeClueIndex === clueLiEls.length-1 ? 0 : ++activeClueIndex;
$('.clues-active').removeClass('.clues-active');
next = $(clueLiEls[activeClueIndex]);
currOri = next.parent().prop('id');
activePosition = $(next).data('position');
// skips over already-solved problems
util.getSkips(activeClueIndex);
activePosition = $(clueLiEls[activeClueIndex]).data('position');
} else {
activeClueIndex = activeClueIndex === clueLiEls.length-1 ? 0 : ++activeClueIndex;
util.getActivePositionFromClassGroup(e.target);
clue = $(clueLiEls + '[data-position=' + activePosition + ']');
activeClueIndex = $(clueLiEls).index(clue);
currOri = clue.parent().prop('id');
}
util.highlightEntry();
util.highlightClue();
//$actives.eq(0).addClass('current');
//console.log('nav.updateByEntry() reports activePosition as: '+activePosition);
}
}; // end nav object
var util = {
highlightEntry: function() {
// this routine needs to be smarter because it doesn't need to fire every time, only
// when activePosition changes
$actives = $('.active');
$actives.removeClass('active');
$actives = $('.position-' + activePosition + ' input').addClass('active');
$actives.eq(0).focus();
$actives.eq(0).select();
},
highlightClue: function() {
var clue;
$('.clues-active').removeClass('clues-active');
$(clueLiEls + '[data-position=' + activePosition + ']').addClass('clues-active');
if (mode === 'interacting') {
clue = $(clueLiEls + '[data-position=' + activePosition + ']');
activeClueIndex = $(clueLiEls).index(clue);
};
},
getActivePositionFromClassGroup: function(el){
classes = util.getClasses($(el).parent(), 'position');
if(classes.length > 1){
// get orientation for each reported position
e1Ori = $(clueLiEls + '[data-position=' + classes[0].split('-')[1] + ']').parent().prop('id');
e2Ori = $(clueLiEls + '[data-position=' + classes[1].split('-')[1] + ']').parent().prop('id');
// test if clicked input is first in series. If so, and it intersects with
// entry of opposite orientation, switch to select this one instead
e1Cell = $('.position-' + classes[0].split('-')[1] + ' input').index(el);
e2Cell = $('.position-' + classes[1].split('-')[1] + ' input').index(el);
if(mode === "setting ui"){
currOri = e1Cell === 0 ? e1Ori : e2Ori; // change orientation if cell clicked was first in a entry of opposite direction
}
if(e1Ori === currOri){
activePosition = classes[0].split('-')[1];
} else if(e2Ori === currOri){
activePosition = classes[1].split('-')[1];
}
} else {
activePosition = classes[0].split('-')[1];
}
console.log('getActivePositionFromClassGroup activePosition: '+activePosition);
},
checkSolved: function(valToCheck) {
for (var i=0, s=solved.length; i < s; i++) {
if(valToCheck === solved[i]){
return true;
}
}
},
getSkips: function(position) {
if ($(clueLiEls[position]).hasClass('clue-done')){
activeClueIndex = position === clueLiEls.length-1 ? 0 : ++activeClueIndex;
util.getSkips(activeClueIndex);
} else {
return false;
}
}
}; // end util object
puzInit.init();
}
})(jQuery);
the answer is simple don't use it

Disable background scrolling when a modal is enabled

I am using a template with the following code to handle scrolling:
Here is the template.
This is the javascript code for scrolling, I can post the html and css if needed but it is large.
// #codekit-prepend "/vendor/hammer-2.0.8.js";
$( document ).ready(function() {
// DOMMouseScroll included for firefox support
var canScroll = true,
scrollController = null;
$(this).on('mousewheel DOMMouseScroll', function(e){
if (!($('.outer-nav').hasClass('is-vis'))) {
e.preventDefault();
var delta = (e.originalEvent.wheelDelta) ? -e.originalEvent.wheelDelta : e.originalEvent.detail * 20;
if (delta > 50 && canScroll) {
canScroll = false;
clearTimeout(scrollController);
scrollController = setTimeout(function(){
canScroll = true;
}, 800);
updateHelper(1);
}
else if (delta < -50 && canScroll) {
canScroll = false;
clearTimeout(scrollController);
scrollController = setTimeout(function(){
canScroll = true;
}, 800);
updateHelper(-1);
}
}
});
$('.side-nav li, .outer-nav li').click(function(){
if (!($(this).hasClass('is-active'))) {
var $this = $(this),
curActive = $this.parent().find('.is-active'),
curPos = $this.parent().children().index(curActive),
nextPos = $this.parent().children().index($this),
lastItem = $(this).parent().children().length - 1;
updateNavs(nextPos);
updateContent(curPos, nextPos, lastItem);
}
});
$('.cta').click(function(){
var curActive = $('.side-nav').find('.is-active'),
curPos = $('.side-nav').children().index(curActive),
lastItem = $('.side-nav').children().length - 1,
nextPos = lastItem;
updateNavs(lastItem);
updateContent(curPos, nextPos, lastItem);
});
// swipe support for touch devices
var targetElement = document.getElementById('viewport'),
mc = new Hammer(targetElement);
mc.get('swipe').set({ direction: Hammer.DIRECTION_VERTICAL });
mc.on('swipeup swipedown', function(e) {
updateHelper(e);
});
$(document).keyup(function(e){
if (!($('.outer-nav').hasClass('is-vis'))) {
e.preventDefault();
updateHelper(e);
}
});
// determine scroll, swipe, and arrow key direction
function updateHelper(param) {
var curActive = $('.side-nav').find('.is-active'),
curPos = $('.side-nav').children().index(curActive),
lastItem = $('.side-nav').children().length - 1,
nextPos = 0;
if (param.type === "swipeup" || param.keyCode === 40 || param > 0) {
if (curPos !== lastItem) {
nextPos = curPos + 1;
updateNavs(nextPos);
updateContent(curPos, nextPos, lastItem);
}
else {
updateNavs(nextPos);
updateContent(curPos, nextPos, lastItem);
}
}
else if (param.type === "swipedown" || param.keyCode === 38 || param < 0){
if (curPos !== 0){
nextPos = curPos - 1;
updateNavs(nextPos);
updateContent(curPos, nextPos, lastItem);
}
else {
nextPos = lastItem;
updateNavs(nextPos);
updateContent(curPos, nextPos, lastItem);
}
}
}
// sync side and outer navigations
function updateNavs(nextPos) {
$('.side-nav, .outer-nav').children().removeClass('is-active');
$('.side-nav').children().eq(nextPos).addClass('is-active');
$('.outer-nav').children().eq(nextPos).addClass('is-active');
}
// update main content area
function updateContent(curPos, nextPos, lastItem) {
$('.main-content').children().removeClass('section--is-active');
$('.main-content').children().eq(nextPos).addClass('section--is-active');
$('.main-content .section').children().removeClass('section--next section--prev');
if (curPos === lastItem && nextPos === 0 || curPos === 0 && nextPos === lastItem) {
$('.main-content .section').children().removeClass('section--next section--prev');
}
else if (curPos < nextPos) {
$('.main-content').children().eq(curPos).children().addClass('section--next');
}
else {
$('.main-content').children().eq(curPos).children().addClass('section--prev');
}
if (nextPos !== 0 && nextPos !== lastItem) {
$('.header--cta').addClass('is-active');
}
else {
$('.header--cta').removeClass('is-active');
}
}
function workSlider() {
$('.slider--prev, .slider--next').click(function() {
var $this = $(this),
curLeft = $('.slider').find('.slider--item-left'),
curLeftPos = $('.slider').children().index(curLeft),
curCenter = $('.slider').find('.slider--item-center'),
curCenterPos = $('.slider').children().index(curCenter),
curRight = $('.slider').find('.slider--item-right'),
curRightPos = $('.slider').children().index(curRight),
totalWorks = $('.slider').children().length,
$left = $('.slider--item-left'),
$center = $('.slider--item-center'),
$right = $('.slider--item-right'),
$item = $('.slider--item');
$('.slider').animate({ opacity : 0 }, 400);
setTimeout(function(){
if ($this.hasClass('slider--next')) {
if (curLeftPos < totalWorks - 1 && curCenterPos < totalWorks - 1 && curRightPos < totalWorks - 1) {
$left.removeClass('slider--item-left').next().addClass('slider--item-left');
$center.removeClass('slider--item-center').next().addClass('slider--item-center');
$right.removeClass('slider--item-right').next().addClass('slider--item-right');
}
else {
if (curLeftPos === totalWorks - 1) {
$item.removeClass('slider--item-left').first().addClass('slider--item-left');
$center.removeClass('slider--item-center').next().addClass('slider--item-center');
$right.removeClass('slider--item-right').next().addClass('slider--item-right');
}
else if (curCenterPos === totalWorks - 1) {
$left.removeClass('slider--item-left').next().addClass('slider--item-left');
$item.removeClass('slider--item-center').first().addClass('slider--item-center');
$right.removeClass('slider--item-right').next().addClass('slider--item-right');
}
else {
$left.removeClass('slider--item-left').next().addClass('slider--item-left');
$center.removeClass('slider--item-center').next().addClass('slider--item-center');
$item.removeClass('slider--item-right').first().addClass('slider--item-right');
}
}
}
else {
if (curLeftPos !== 0 && curCenterPos !== 0 && curRightPos !== 0) {
$left.removeClass('slider--item-left').prev().addClass('slider--item-left');
$center.removeClass('slider--item-center').prev().addClass('slider--item-center');
$right.removeClass('slider--item-right').prev().addClass('slider--item-right');
}
else {
if (curLeftPos === 0) {
$item.removeClass('slider--item-left').last().addClass('slider--item-left');
$center.removeClass('slider--item-center').prev().addClass('slider--item-center');
$right.removeClass('slider--item-right').prev().addClass('slider--item-right');
}
else if (curCenterPos === 0) {
$left.removeClass('slider--item-left').prev().addClass('slider--item-left');
$item.removeClass('slider--item-center').last().addClass('slider--item-center');
$right.removeClass('slider--item-right').prev().addClass('slider--item-right');
}
else {
$left.removeClass('slider--item-left').prev().addClass('slider--item-left');
$center.removeClass('slider--item-center').prev().addClass('slider--item-center');
$item.removeClass('slider--item-right').last().addClass('slider--item-right');
}
}
}
}, 400);
$('.slider').animate({ opacity : 1 }, 400);
});
}
function transitionLabels() {
$('.work-request--information input').focusout(function(){
var textVal = $(this).val();
if (textVal === "") {
$(this).removeClass('has-value');
}
else {
$(this).addClass('has-value');
}
// correct mobile device window position
window.scrollTo(0, 0);
});
}
outerNav();
workSlider();
transitionLabels();
});
How can I disable this code so the background doesn't scroll when an elements display is set to "block" meaning a modal is present?
Sorry for being vague if you need more specifics let me know!
EDIT 1:
I have tried disabled the div using:
$(".l-viewport").attr('disabled','disabled');
I have set the z-index of the model above all else
you can create a class HideScroll in your css:
.HideScroll {
overflow-y: hidden !important;
overflow-x: hidden !important;
}
The in the code that displays your modal, add this css to your main div:
$('.yourMainDivClass').addClass('HideScroll')
upon modal close, remove the class:
$('.yourMainDivClass').removeClass('HideScroll')
you can also use jquery toggleClass function.
OR
you can wrap your main div inside <fieldset> and set it's disabled attribute to true:
<fieldset id="fs-1">
<div id="yourMainDiv"></div>
</fieldset>
upon showing modal:
$('#fs-1').attr('disabled', true);
upon closing modal:
$('#fs-1').removeAttr('disabled');

Create a javascript object

I would say I am fairly decent with javascript and jQuery, well, enough to get the job done and done pretty well.
I do however lack a deep understanding of js.
I have created some functions for highlighting table elements.
ctrl+click toggles selections
shift+click+drag highlights selection
My question does not pertain to whether or not my implementation is the best way or not but ...
How do I abstract this functionality so I can add this functionality to any table. Like if I add more highlighting features and such and put this in its own .js file. How would I attach it to any html table?
Sorry if this has already been answered, but I could not think of what to search for.
Thank you.
****Newest Code****
This code is in its own .js file and is attached to my table.
All the current functionality is there. The only thing I am weary of is the .off() functions. In my case, I am reloading new tables.... as I type this, I realize I should just empty the tr's from the table instead of recreating a new table all the time, then I could get rid of the .off() calls.
$.fn.addEvents = function(obj)
{
console.log("Adding events to table");
var properties = $.extend(true,
{
shifting: false,
ctrling: false,
mousing: false,
mouseenter: 0,
mouseleave: 0,
mousestartindex: 0,
mouseenterindex: 0,
mouseleaveindex: 0,
trajectory: null,
tmptrajectory: null
}, obj || {});
$(document)
.off('mouseup')
.on('mouseup', function(e)
{
properties.mousing = false;
properties.trajectory = null;
})
.off("keyup")
.on("keyup", function(e)
{
if(e.which == 16)
{
properties.shifting = false;
}
if(e.which == 17)
{
properties.ctrling = false;
}
})
.off("keydown")
.on("keydown", function(e)
{
if(e.which == 16)
{
properties.shifting = true;
}
if(e.which == 17)
{
properties.ctrling = true;
}
if($(this).find('tr.selected').length > 0)
{
switch(e.which)
{
//case 37: // left
//break;
case 38: // up
var index = $(this).find('tr.selected').index();
if(index > 0)
{
$(this).find('tr').removeClass('selected');
$(this).find('tr td').removeClass('selected');
$(this).find('tr:eq(' + index + ')').addClass('selected');
$(this).find('tr:eq(' + index + ') td').addClass('selected');
}
break;
//case 39: // right
//break;
case 40: // down
var index = $(this).find('tr.selected').index();
if(index < $(this).find('tr').length - 2)
{
$(this).find('tr').removeClass('selected');
$(this).find('tr td').removeClass('selected');
$(this).find('tr:eq(' + (index+2) + ')').addClass('selected');
$(this).find('tr:eq(' + (index+2) + ') td').addClass('selected');
}
break;
case 117: // f6
var index = $(this).find('tr.selected').index();
if(index > 0)
{
....
}
break;
case 118: // f7
var index = $(this).find('tr.selected').index();
if(index < $(this).find('tr').length - 1)
{
....
}
break;
default: return; // exit this handler for other keys
}
e.preventDefault(); // prevent the default action (scroll / move caret)
}
return;
});
return $(this)
.off('click')
.off('contextmenu')
.on('click', function()
{
if(!properties.ctrling && !properties.shifting)
{
$('#datatablebody tr, #datatablebody tr td').removeClass('selected');
$(this).addClass('selected');
$(this).find('td').addClass('selected');
}
else if(properties.ctrling && $(this).hasClass('selected'))
{
$(this).removeClass('selected');
$(this).find('td').removeClass('selected');
}
else if(properties.ctrling && !$(this).hasClass('selected'))
{
$(this).addClass('selected');
$(this).find('td').addClass('selected');
}
})
.on('contextmenu', function(ev)
{
ev.preventDefault();
$('#datatablebody tr, #datatablebody tr td').removeClass('selected');
$(this).addClass('selected');
$(this).find('td').addClass('selected');
showContextMenuTR($(this).closest('tr').attr('id'), ev.clientX, ev.clientY);
return false;
})
.off('mousedown')
.on('mousedown', function(e)
{
properties.mousing = true;
properties.mousestartindex = $(this).index();
if(properties.shifting && properties.mousing)
{
multiselectrow($(this));
}
})
.off('mouseenter')
.on('mouseenter', function(e)
{
properties.mouseenter = e.clientY;
properties.mouseenterindex = $(this).index();
if(properties.tmptrajectory === properties.trajectory)
{
if(properties.shifting && properties.mousing)
{
multiselectrow($(this));
}
}
})
.off('mouseleave')
.on('mouseleave', function(e)
{
properties.mouseleave = e.clientY;
if(properties.shifting && properties.mousing)
{
properties.tmptrajectory = properties.mouseenter - properties.mouseleave < 0?1:-1;
}
if(properties.trajectory != null && properties.tmptrajectory !== properties.trajectory && $(this).index() !== properties.mousestartindex)
{
if(properties.shifting && properties.mousing)
{
multiselectrow($(this));
}
}
if(properties.shifting && properties.mousing)
{
if(properties.trajectory == null)
{
properties.trajectory = properties.tmptrajectory;
}
else if(properties.tmptrajectory !== properties.trajectory && $(this).index() === properties.mousestartindex)
{
properties.trajectory = properties.tmptrajectory;
}
}
})
.off('mouseup')
.on('mouseup', function(e)
{
properties.mousing = false;
properties.trajectory = null;
if(properties.shifting && properties.mousing)
{
multiselectrow($(this));
}
});
}
function multiselectrow(obj)
{
if($(obj).hasClass('selected'))
{
$(obj).removeClass('selected');
$(obj).find('td').removeClass('selected');
}
else
{
$(obj).addClass('selected');
$(obj).find('td').addClass('selected');
}
}
you can wrap all this in a function since you have some local variables related to individual selection
$.fn.addEvents = function(obj) {
var properties = $.extend(true, {
shifting: false,
ctrling: false,
mousing: false,
mouseenter: 0,
mouseleave: 0,
trajectory: null
}, obj || {});
return $(this)
.off('click')
.off('contextmenu')
.on('click', function() {
.....
})
.on('mouseleave', function(e) {
//rename your local variables with `properties.` prefix
properties.mouseleave = e.clientY;
if (properties.shifting && properties.mousing) {
tmptrajectory = properties.mouseenter - properties.mouseleave < 0 ? 1 : -1;
}
if ($(this).hasClass('selected') && properties.shifting && properties.mousing && properties.trajectory != null && properties.trajectory != tmptrajectory) {
$(this).removeClass('selected');
$(this).find('td').removeClass('selected');
}
....
});
}
usage
$('#datatablebody tr').addEvents({ shifting: false, ctrling: true }); //custom settings
$('#someother tr').addEvents(); //default settings
you could add that functionality to a class and add that class to the tables you want to affect...
Here I create the class .myTableBeh and all tables with that class will have the behaviour you programmed.
var shifting = false;
var ctrling = false;
var mousing = false;
var mouseenter = 0;
var mouseleave = 0;
var trajectory = null;
$('.myTableBeh tr')
.off('click')
.off('contextmenu')
.on('click', function()
{
if(!ctrling)
{
$('.myTableBeh tr, .myTableBeh tr td').removeClass('selected');
$(this).addClass('selected');
$(this).find('td').addClass('selected');
}
else if(ctrling && $(this).hasClass('selected'))
{
$(this).removeClass('selected');
$(this).find('td').removeClass('selected');
}
else if(ctrling && !$(this).hasClass('selected'))
{
$(this).addClass('selected');
$(this).find('td').addClass('selected');
}
})
.on('contextmenu', function(ev)
{
ev.preventDefault();
$('.myTableBeh tr, .myTableBeh tr td').removeClass('selected');
$(this).addClass('selected');
$(this).find('td').addClass('selected');
showContextMenuTR($(this).closest('tr').attr('id'), ev.clientX, ev.clientY);
return false;
})
.off('mousedown')
.on('mousedown', function(e)
{
mousing = true;
multiselectrow($(this));
})
.off('mouseenter')
.on('mouseenter', function(e)
{
mouseenter = e.clientY;
multiselectrow($(this));
})
.off('mouseleave')
.on('mouseleave', function(e)
{
mouseleave = e.clientY;
if(shifting && mousing)
{
tmptrajectory = mouseenter - mouseleave < 0?1:-1;
}
if($(this).hasClass('selected') && shifting && mousing && trajectory != null && trajectory != tmptrajectory)
{
$(this).removeClass('selected');
$(this).find('td').removeClass('selected');
}
if(shifting && mousing && trajectory == null)
{
trajectory = tmptrajectory;
}
})
.off('mouseup')
.on('mouseup', function(e)
{
mousing = false;
trajectory = null;
multiselectrow($(this));
});
Thanks to the answer from #JAG I was able to create a nice add on to any HTML table that handles highlighting.
Check out the fiddle for the working version and please use it if you find it helpful or useful for your site.
You can even implement keys to move the tr position up or down in the table. I removed my implementation because it was specific to the project I am working on.
I made it so you have to mouse over the table in order to interact with it and mouse leave to un-focus it.
https://jsfiddle.net/kwj74kg0/2/
//Add the events simply by running this
$('#dtable tr').addEvents(0);
/**
* This add on can be applied and customized to any html tr set i.e. $('#tableid tr').addEvents()
* It will add highlighting capability to the table.
*
* Single click highlight tr
* Click -> Shift + click highlight/toggle range
* Shift+MouseDown+Drag highlight/toggle range
* Ctrl+Click toggle item
*
*
* #author Michaela Ervin
*
* #param tabindex
*
* Help from JAG on http://stackoverflow.com/questions/39022116/create-a-javascript-object
*/
$.fn.addEvents = function(tabindex)
{
console.log("Adding events to table");
var properties = $.extend(true,
{
shifting: false,
ctrling: false,
mousing: false,
mouseenter: 0,
mouseleave: 0,
mousestartindex: 0,
mouseenterindex: null,
mouseleaveindex: null,
trajectory: null,
tmptrajectory: null
}, {});
/**
* Add events to closest table.
*/
$(this)
.closest('table')
.attr('tabindex', tabindex)
.off('mouseenter')
.on('mouseenter', function()
{
$(this).focus();
})
.off('mouseleave')
.on('mouseleave', function()
{
$(this).blur();
properties.mousing = false;
properties.trajectory = null;
properties.mouseenterindex = null;
properties.mouseleaveindex = null;
properties.mouseintermediateindex = null;
})
.off("keyup")
.on("keyup", function(e)
{
if(e.which == 16)
{
properties.shifting = false;
}
if(e.which == 17)
{
properties.ctrling = false;
}
})
.off("keydown")
.on("keydown", function(e)
{
if(e.which == 16)
{
properties.shifting = true;
}
if(e.which == 17)
{
properties.ctrling = true;
}
if($(this).find('tr.selected').length > 0)
{
switch(e.which)
{
//case 37: // left
//break;
case 38: // up
var index = $(this).find('tr.selected').index();
if(index > 0)
{
$(this).find('tr').removeClass('selected');
$(this).find('tr td').removeClass('selected');
$(this).find('tr:eq(' + index + ')').addClass('selected');
$(this).find('tr:eq(' + index + ') td').addClass('selected');
}
break;
//case 39: // right
//break;
case 40: // down
var index = $(this).find('tr.selected').index();
if(index < $(this).find('tr').length - 2)
{
$(this).find('tr').removeClass('selected');
$(this).find('tr td').removeClass('selected');
$(this).find('tr:eq(' + (index+2) + ')').addClass('selected');
$(this).find('tr:eq(' + (index+2) + ') td').addClass('selected');
}
break;
case 117: // f6
var index = $(this).find('tr.selected').index();
if(index > 0)
{
//Function to move tr 'up'.
}
break;
case 118: // f7
var index = $(this).find('tr.selected').index();
if(index < $(this).find('tr').length - 1)
{
//Function to move tr 'down'.
}
break;
default: return; // exit this handler for other keys
}
e.preventDefault(); // prevent the default action (scroll / move caret)
}
return;
});
/**
* Add tr specific events
*/
return $(this)
.off('click')
.on('click', function()
{
if(!properties.shifting && properties.mouseenterindex != null)
{
properties.mouseenterindex = null;
properties.mousing = false;
}
if(!properties.ctrling && !properties.shifting && properties.mouseenterindex == null)
{
$(this).parent().find('tr').removeClass('selected');
$(this).parent().find('tr td').removeClass('selected');
$(this).addClass('selected');
$(this).find('td').addClass('selected');
}
else if(properties.ctrling && $(this).hasClass('selected'))
{
$(this).removeClass('selected');
$(this).find('td').removeClass('selected');
}
else if(properties.ctrling && !$(this).hasClass('selected'))
{
$(this).addClass('selected');
$(this).find('td').addClass('selected');
}
if(properties.mouseenterindex == null)
{
properties.mouseenterindex = $(this).index();
}
else if(properties.shifting && properties.mouseenterindex != null)
{
properties.mouseleaveindex = $(this).index();
highlightRange($(this).parent(), properties.mouseenterindex, properties.mouseleaveindex, properties.mouseenterindex);
properties.mouseenterindex = null;
properties.mouseleaveindex = null;
}
})
.off('contextmenu')
.on('contextmenu', function(ev)
{
ev.preventDefault();
$(this).parent().find('tr').removeClass('selected');
$(this).parent().find('tr td').removeClass('selected');
$(this).addClass('selected');
$(this).find('td').addClass('selected');
//Put your context menu here
return false;
})
.off('mousedown')
.on('mousedown', function(e)
{
properties.mousing = true;
properties.mousestartindex = $(this).index();
if(properties.shifting && properties.mousing && properties.mouseenterindex == null)
{
multiselectrow($(this));
}
})
.off('mouseenter')
.on('mouseenter', function(e)
{
properties.mouseenter = e.clientY;
if(properties.tmptrajectory === properties.trajectory)
{
if(properties.shifting && properties.mousing)
{
multiselectrow($(this));
}
}
})
.off('mouseleave')
.on('mouseleave', function(e)
{
properties.mouseleave = e.clientY;
if(properties.shifting && properties.mousing)
{
properties.tmptrajectory = properties.mouseenter - properties.mouseleave < 0?1:-1;
}
if(properties.trajectory != null && properties.tmptrajectory !== properties.trajectory && $(this).index() !== properties.mousestartindex)
{
if(properties.shifting && properties.mousing)
{
multiselectrow($(this));
}
}
if(properties.shifting && properties.mousing)
{
if(properties.trajectory == null)
{
properties.trajectory = properties.tmptrajectory;
}
else if(properties.tmptrajectory !== properties.trajectory && $(this).index() === properties.mousestartindex)
{
properties.trajectory = properties.tmptrajectory;
}
}
})
.off('mouseup')
.on('mouseup', function(e)
{
properties.mousing = false;
properties.trajectory = null;
if(!properties.shifting)
{
properties.mouseenterindex = null;
properties.mouseleaveindex = null;
}
});
}
function multiselectrow(obj)
{
if($(obj).hasClass('selected'))
{
$(obj).removeClass('selected');
$(obj).find('td').removeClass('selected');
}
else
{
$(obj).addClass('selected');
$(obj).find('td').addClass('selected');
}
}
function highlightRange(obj, start, end, mouseenterindex)
{
if(start < end)
{
for(var i=start; i<=end; i+=1)
{
if(i !== mouseenterindex)
{
multiselectrow($(obj).find('tr').eq(i));
}
}
}
else
{
for(var i=start; i>=end; i-=1)
{
if(i !== mouseenterindex)
{
multiselectrow($(obj).find('tr').eq(i));
}
}
}
}

clearInterval not clearing the interval

Thanks for the response. I have solved my problem. I really did see that its a list of callback functions. After some work i managed to shoot by intervals, but the first shot was after 1 second.
1 - a problem - if I call the function in setInterval imidiatly and then set interval - shoots rapidly.
2 - I fixed the problem by making setTimeout to set a bool value hasShooted to false after 1 second and if that value is false i can shoot. In the function i do that i set it to true.
3 - I realized I need only that last function with set timeout and not setInterval at all.
var PlayerManager = (function(parent){
'use strict';
var bulletPossLeft,
bulletPossTop,
FIRE_SPEED = 1000,
hasShot = false;
PlayerManager.prototype = Object.create(parent.prototype);
function PlayerManager() {
parent.call(this);
this.moveLeft= false;
this.moveRight= false;
this.moveForward= false;
this.moveBack= false;
this.isShooting= false;
this.bulletManager = new BulletManager();
}
PlayerManager.prototype.onGameLoop = function(obj) {
if (this.isShooting) {
bulletPossLeft = obj.positionLeft + Math.floor(obj.planeWidth /2);
bulletPossTop = obj.positionTop - Math.ceil(obj.planeHeight /2);
if(!hasShot){
this.shoot();
hasShot = true;
setTimeout(function(){
hasShot = false;
}, FIRE_SPEED);
}
}
if (this.moveLeft && (obj.positionLeft - obj.speed) > 0) {
obj.positionLeft -= obj.speed;
}
if (this.moveRight && (obj.positionLeft + obj.speed) < Game.getContextValue('width')) {
obj.positionLeft += obj.speed;
}
if (this.moveForward && (obj.positionTop - obj.speed) > 0) {
obj.positionTop -= obj.speed;
}
if (this.moveBack && (obj.positionTop + obj.speed) < Game.getContextValue('height')) {
obj.positionTop += obj.speed;
}
obj.move();
};
PlayerManager.prototype.shoot = function(){
this.bulletManager.spawn(new Bullet(bulletPossLeft, bulletPossTop, 'orange'));
};
PlayerManager.prototype.keyboardListener = function(e) {
var value = e.type == 'keydown';
switch (e.keyCode) {
case 37:
this.moveLeft = value;
break;
case 38:
this.moveForward = value;
break;
case 39:
this.moveRight = value;
break;
case 40:
this.moveBack = value;
break;
case 32:
this.isShooting = value;
break;
default:
break;
}
};
return PlayerManager;
})(Manager);
You are setting a new interval every time onGameLoop is executed and this.isShooting equals to true. Therefore when you use clearInterval, you are clearing only the last interval, not all of them.
I recommend you clearing the variable shootInterval after clearing interval (for example: shootInterval = null;) and in the first condition (if (this.isShooting)) check if shootInterval is not null.
Your code should look like this:
var bulletPossLeft,
bulletPossTop,
fireSpeed = 1000,
shootInterval,
self;
PlayerManager.prototype = Object.create(parent.prototype);
function PlayerManager() {
parent.call(this);
this.moveLeft= false;
this.moveRight= false;
this.moveForward= false;
this.moveBack= false;
this.isShooting= false;
this.bulletManager = new BulletManager();
self = this;
}
PlayerManager.prototype.onGameLoop = function(obj) {
if (this.isShooting && shootInterval == null) {
bulletPossLeft = obj.positionLeft + Math.floor(obj.planeWidth /2);
bulletPossTop = obj.positionTop - Math.ceil(obj.planeHeight /2);
shootInterval = setInterval(function(){
self.shoot();
} , fireSpeed);
}
if(!this.isShooting) {
clearInterval(shootInterval);
shootInterval = null;
}
if (this.moveLeft && (obj.positionLeft - obj.speed) > 0) {
obj.positionLeft -= obj.speed;
debugger;
}
if (this.moveRight && (obj.positionLeft + obj.speed) < Game.getContextValue('width')) {
obj.positionLeft += obj.speed;
}
if (this.moveForward && (obj.positionTop - obj.speed) > 0) {
obj.positionTop -= obj.speed;
}
if (this.moveBack && (obj.positionTop + obj.speed) < Game.getContextValue('height')) {
obj.positionTop += obj.speed;
}
obj.move();
};
PlayerManager.prototype.shoot = function(){
this.bulletManager.spawn(new Bullet(bulletPossLeft, bulletPossTop, 'orange'));
};
PlayerManager.prototype.keyboardListener = function(e) {
var value = e.type == 'keydown';
switch (e.keyCode) {
case 37:
this.moveLeft = value;
break;
case 38:
this.moveForward = value;
break;
case 39:
this.moveRight = value;
break;
case 40:
this.moveBack = value;
break;
case 32:
this.isShooting = true;
break;
default:
break;
}
if(e.type == 'keyup'){
this.isShooting = false;
}
};
return PlayerManager;
})(Manager);

Too much recursion Error in jQuery-1.7.2,is jquery error?

too much recursion error occur when i execute autocomplete.js today.Before today i never see like this error in jquery when i execute autocomplete.js. i am using jQuery 1.7.2
$(function(){
$("#search_text").keyup(function(e){
var sVal = $(this).val();
if(e.which == 27) {
$('#sresult_container').remove();
return;
}
if(e.which != 40 && e.which != 38) {
$("#search").removeAttr('disabled');
$.post('http://localhost/website/index.php/search/ajaxResults',{Search:sVal},function(data){
if(data != "$$$" && data.length != 0) {
var sData = data;
var flag1 = 0;
var flag2 = 0;
var tabindex = -1;
var aFarray = sData.split('$$$');
$('#sresult_container').remove();
var $sresult_container = $('<div id="sresult_container"></div>')
.css({'position':'absolute','border':'1px solid','background-color':'white','z-index':'10000000','width':'309px'});
for(var i=0;i<aFarray.length;i++) {
var a = aFarray[i].split('|||');
if(i == 0 && a[0] != "") {
flag1 = 1;
$pages = $('<div id="pages"></div>');
$text1 = $('<p></p>').css({'background-color':'silver','text-align':'center','padding':'3px'}).text("Pages");
$pages.append($text1);
if(a.length > 5) {
a = a.slice(0,5);
}
for(var j=1;j<a.length+1;j++) {
tabindex++;
$('<div>/div>').css({'padding':'5px','text-align':'center'}).text(a[j-1]).attr({'tabindex':tabindex,'class':'result'}).appendTo($pages);
}
}
if(i == 1 && a[0] != "") {
flag2 = 1;
$articles = $('<div id="articles"></div>');
$text2 = $("<p></p>").css({'background-color':'silver','text-align':'center','padding':'3px'}).text("Articles");
$articles.append($text2);
if(a.length > 5) {
a = a.slice(0,5);
}
for(var j=0;j<a.length;j++) {
tabindex++;
$('<div></div>').css({'padding':'5px','text-align':'center'}).text(a[j]).attr({'tabindex':tabindex,'class':'result'}).appendTo($articles);
}
}
}
if(flag1 == 0)
{
$articles.children().first().remove();
$div = $sresult_container.append($articles);
}else if(flag2 == 0)
{
$pages.children().first().remove();
$div = $sresult_container.append($pages);
}else
{
$div = $sresult_container.append($pages,$articles);
}
tabindex++;
$allresluts = $('<div id="allresults"></div>').css({'padding':'5px','text-align':'center','background-color':'#FBEE92','color':'#CC3333'}).text("See All Results").attr('tabindex',tabindex).appendTo($div);
var bottom = $('#search_text').offset();
var height = $('#search_text').outerHeight();
var left = bottom.left;
var top = bottom.top+height;
$div.offset({'top':top,'left':left});
$('body').append($div);
}
else
{
$('#sresult_container').remove();
$("#search").attr('disabled','true');
}
});
}
else
{
$con_div = $('#sresult_container').children().children('div').add($('#sresult_container').children().last());
var tabindex = $con_div.length - 1;
if(e.which == 40)
{
$con_div.first().addClass("selected").focus();
var index = $con_div.first().index(this)+1;
$con_div.bind({
keydown: function(e) {
e.preventDefault();
var key = e.keyCode;
var target = $(e.target);
switch(key) {
case 38: // arrow up
if(index == 0)
{
index = tabindex+1;
}
$con_div[--index].focus();
break;
case 40: // arrow down
if(index > tabindex-1)
{
index = -1;
}
$con_div[++index].focus();
break;
case 13: //Enter
if(target.hasClass('result') == true)
{
$("#search_text").val(target.text());
$("#search").focus();
}
else
{
$('#search').click();
}
$div.remove();
break;
case 27://Esc
$div.remove();
$("#search_text").focus();
break;
}
},
focusin: function(e) {
$(e.currentTarget).addClass("selected");
},
focusout: function(e) {
$con_div.removeClass("selected");
$(e.currentTarget).removeClass("selected");
}
});
}
}
setTimeout(function()
{
$con_div = $('#sresult_container').children().children('div').add($('#sresult_container').children().last());
$con_div.live({
click : function(e){
var $target = $(e.target);
if($target.hasClass('result') == true)
{
$("#search_text").val($target.text());
$("#search").focus();
}
else
{
$('#search').click();
}
$('#sresult_container').remove();
},
mouseover : function(e){
var $target = $(e.target);
if($target.hasClass('result') == true || $target.is('#allresults'))
{
$(e.target).css('cursor','pointer');
$con_div.removeClass("selected");
$(e.target).addClass("selected");
}
},
mouseout : function(){
$con_div.removeClass("selected");
}
});
}, 200 );
});
$("#search_text").blur(function(e){
$con_div = $('#sresult_container').children().children('div').add($('#sresult_container').children().last());
if($con_div.hasClass('selected') != true)
{
$("#sresult_container").remove();
}
});
});
I got error in $('#search').click(); inside the code.

Categories