Applying KeyUp and KeyDown using Javascript - javascript

I am new to JS and need your help with this issue please. I need to apply key-up and key-down using HTML, CSS and JS. I have the original code and I am trying to modify it so I can move through the panel using key-up and key-down, but it does not seem to work at all. Here is a snippet of the original code for the part I am working on:
const P_CONTAINERS_LIST = "containersList";
const P_CONTAINER_EDIT = "containerEdit";
const P_CONTAINER_DELETE = "containerDelete";
Logic.registerPanel(P_CONTAINERS_EDIT, {
panelSelector: "#edit-containers-panel",
// This method is called when the object is registered.
initialize() {
Logic.addEnterHandler(document.querySelector("#exit-edit-mode-link"), () => {
Logic.showPanel(P_CONTAINERS_LIST);
});
},
// This method is called when the panel is shown.
prepare() {
const fragment = document.createDocumentFragment();
Logic.identities().forEach(identity => {
const tr = document.createElement("tr");
fragment.appendChild(tr);
tr.classList.add("container-panel-row");
tr.innerHTML = escaped`
<td class="userContext-wrapper">
<div class="userContext-icon-wrapper">
<div class="usercontext-icon"
data-identity-icon="${identity.icon}"
data-identity-color="${identity.color}">
</div>
</div>
<div class="container-name truncate-text"></div>
</td>
<td class="edit-container pop-button edit-container-icon">
<img
src="/img/container-edit.svg"
class="pop-button-image" />
</td>
<td class="remove-container pop-button delete-container-icon">
<img
class="pop-button-image"
src="/img/container-delete.svg"
/>
</td>`;
tr.querySelector(".container-name").textContent = identity.name;
tr.querySelector(".edit-container").setAttribute("title", `Edit ${identity.name} container`);
tr.querySelector(".remove-container").setAttribute("title", `Remove ${identity.name} container`);
Logic.addEnterHandler(tr, e => {
if (e.target.matches(".edit-container-icon") || e.target.parentNode.matches(".edit-container-icon")) {
Logic.showPanel(P_CONTAINER_EDIT, identity);
} else if (e.target.matches(".delete-container-icon") || e.target.parentNode.matches(".delete-container-icon")) {
Logic.showPanel(P_CONTAINER_DELETE, identity);
}
});
});
const list = document.querySelector("#edit-identities-list");
list.innerHTML = "";
list.appendChild(fragment);
return Promise.resolve(null);
},
});
<div class="scrollable panel-content" tabindex="-1">
<table class="unstriped">
<tbody id="edit-identities-list"></tbody>
</table>
</div>
I tried to insert the following part in my JS code, but the problem still exists:
document.addEventListener("keydown", (e) => {
const selectables = [...document.querySelectorAll("[tabindex='0'], [tabindex='-1']")];
const element = document.activeElement;
const index = selectables.indexOf(element) || 0;
function next() {
const nextElement = selectables[index + 1];
if (nextElement) {
nextElement.focus();
}
}
function previous() {
const previousElement = selectables[index - 1];
if (previousElement) {
previousElement.focus();
}
}
switch (e.keyCode) {
case 40:
next();
break;
case 38:
previous();
break;
default:
if ((e.keyCode >= 49 && e.keyCode <= 57) &&
Logic._currentPanel === "containerEdit") {
const element = selectables[e.keyCode - 48];
if (element) {
element.click();
}
}
break;
}
});
Thank you very much.

Your sample seems functional, so I suspect your issue relates to whatever Logic._currentPanel is and the concept of document.activeElement in that respect.
For selectables.indexOf(element) results -1 when element it is out of index or not known, not null or undefined, so const index = selectables.indexOf(element) || 0; won't be 0;
document.addEventListener("keydown", (e) => {
const selectables = [...document.querySelectorAll("[tabindex='0'], [tabindex='-1']")];
const element = document.activeElement;
const index = selectables.indexOf(element) || 0;
function next() {
const nextElement = selectables[index + 1];
if (nextElement) {
nextElement.focus();
}
}
function previous() {
const previousElement = selectables[index - 1];
if (previousElement) {
previousElement.focus();
}
}
switch (e.keyCode) {
case 40:
next();
break;
case 38:
previous();
break;
default:
if ((e.keyCode >= 49 && e.keyCode <= 57)) {
const element = selectables[e.keyCode - 48];
if (element) {
element.click();
}
}
break;
}
});
div {
display:block;
padding: 1in;
border:0.1em solid black;
}
div:focus {
box-shadow: 0px 0px 10px green;
}
<div tabindex="-1">
1
</div>
<div tabindex="-1">
2
</div>

Related

Changing photos in the gallery using touchMove and click event

I'm building a website and I have a problem in one of the steps. I display the photo gallery, after click the button the photo resize to the whole page, then I use the buttons to move the previous and next photos, everything works. I want to add functionality on a mobile device in swapping photo, but I have a problem with that.
If I select photo [2] I can only scroll pictures forward [3] and back [1]. I can't even display the selected photo[2].
Can someone help and point out what I'm doing wrong?
I’ve just started learning JavaScript.
[1]: https://codepen.io/AdamXodeo/pen/rNrgmom
https://codepen.io/AdamXodeo/pen/rNrgmom
const photos = Array.from(document.querySelectorAll(".gallery_item img"));
const popup = document.querySelector(".popup");
const popup_close = document.querySelector(".popup_close");
const popup_img = document.querySelector(".popup_img");
const arrow_left = document.querySelector(".popup_arrow--left");
const arrow_right = document.querySelector(".popup_arrow--right");
let currentImgIndex=0;
let isDragging=false;
let startPos=0;
let currentTranslate=0;
let prevTranslate=0;
let animationID=0;
photos.forEach((photo, index) => {
const showPopup = (e) => {
popup.classList.remove("hidden");
popup_img.src = e.target.src;
currentImgIndex = index;
};
photo.addEventListener("click", showPopup);
// touch events
popup_img.addEventListener("touchstart", touchStart(index));
popup_img.addEventListener("touchend", touchEnd);
popup_img.addEventListener("touchmove", touchMove);
});
const showNextImg = () => {
if (currentImgIndex === photos.length - 1) {
currentImgIndex = 0;
} else {
currentImgIndex++;
}
popup_img.src = photos[currentImgIndex].src;
};
const showPreviousImg = () => {
if (currentImgIndex === 0) {
currentImgIndex = photos.length - 1;
} else {
currentImgIndex--;
}
popup_img.src = photos[currentImgIndex].src;
};
const closePopup = () => {
popup.classList.add("fade-out");
setTimeout(() => {
popup.classList.add("hidden");
popup.classList.remove("fade-out");
}, 300);
};
document.addEventListener("keydown", (e) => {
if (!popup.classList.contains("hidden")) {
if (e.code === "ArrowRight" || e.keyCode === 39) {
showNextImg();
}
if (e.code === "ArrowLeft" || e.keyCode === 37) {
showPreviousImg();
}
if (e.code === "Escape" || e.keyCode === 27) {
closePopup();
}
}
});
popup_close.addEventListener("click", closePopup);
arrow_right.addEventListener("click", showNextImg);
arrow_left.addEventListener("click", showPreviousImg);
function touchStart(index){
return function (event) {
currentImgIndex = index;
startPos = getPositionX(event);
isDragging = true;
popup.classList.add("grabbing");
}
}
function touchMove(event){
if(isDragging){
const currentPosition = getPositionX(event);
currentTranslate = prevTranslate + currentPosition - startPos;
}
}
function touchEnd(){
isDragging = false;
const movedBy = currentTranslate - prevTranslate;
if(movedBy < -30 && currentImgIndex < photos.length - 1) currentImgIndex += 1 ;
if(movedBy > 30 && currentImgIndex > 0) currentImgIndex -= 1;
changePhotoByIndex();
popup.classList.remove("grabbing");
}
function getPositionX(event){
return event.type.includes("mouse") ? event.pageX : event.touches[0].clientX;
}
function animation() {
if (isDragging) requestAnimationFrame(animation);
}
function changePhotoByIndex() {
popup_img.src = photos[currentImgIndex].src;
prevTranslate = currentTranslate;
}
const prevButton = document.querySelector('#prev');
const nextButton = document.querySelector('#next');
const images = document.querySelectorAll('.gallery img');
let currentImage = 0;
images[currentImage].classList.add('active');
prevButton.addEventListener('click', function() {
images[currentImage].classList.remove('active');
currentImage--;
if (currentImage < 0) {
currentImage = images.length - 1;
}
images[currentImage].classList.add('active');
});
nextButton.addEventListener('click', function() {
images[currentImage].classList.remove('active');
currentImage++;
if (currentImage >= images.length) {
currentImage = 0;
}
images[currentImage].classList.add('active');
});
.gallery {
display: flex;
}
.gallery img {
width: 100%;
height: auto;
display: none;
}
.gallery .active {
display: block;
}
<div class="gallery">
<img src="https://www.pakainfo.com/wp-content/uploads/2021/09/dummy-user-image-url-300x200.jpg" class="active">
<img src="https://www.pakainfo.com/wp-content/uploads/2021/09/live-image-link-300x157.jpg">
<img src="https://www.pakainfo.com/wp-content/uploads/2021/09/online-dummy-image-url-300x201.jpg">
<img src="https://www.pakainfo.com/wp-content/uploads/2021/09/test-image-online-300x148.jpg">
<img src="https://www.pakainfo.com/wp-content/uploads/2021/09/sample-image-url-link-300x300.png">
</div>
<button id="prev">Previous</button>
<button id="next">Next</button>

Different way to know the origin of the click, optimizing my tab system

I'm learning JavaScript, and I've done this code, I just want to be sure that I'm on the right way, would you have done it differently?
What bothers me is especially to have passed parameters to the functions "next" and "updateArrows" to know the origin of the clicks
const changeStep = document.querySelectorAll(".step");
const currentPaginate = document.querySelector(".pagination span.active");
const arrows = document.querySelectorAll(".arrow");
for (let arrow of arrows) {
arrow.addEventListener("click", function () {
updateArrows(arrow);
});
}
for (let step of changeStep) {
step.addEventListener("click", function () {
next(step);
});
}
function updateArrows(arrow, currentStep = null, update = true) {
let nextStep;
if (currentStep == null) {
currentStep = document.querySelector(".step.current");
if (arrow.classList.contains("arrow-bottom")) {
nextStep = currentStep.nextElementSibling;
} else {
nextStep = currentStep.previousElementSibling;
}
} else nextStep = document.querySelector(".step.current");
if (!arrow.classList.contains("impossible")) {
if (nextStep.dataset.id != 1 && nextStep.dataset.id != 5) {
arrows.forEach(function (arrow) {
if (arrow.classList.contains("impossible")) {
arrow.classList.remove("impossible");
}
});
} else if (nextStep.dataset.id == 5) {
if (arrow.previousElementSibling.classList.contains("impossible"))
arrow.previousElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
} else if (nextStep.dataset.id == 1) {
if (arrow.nextElementSibling.classList.contains("impossible"))
arrow.nextElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
}
if (update == true) next(nextStep, false);
}
}
function next(step, update = true) {
if (!step.classList.contains("current")) {
const currentStep = document.querySelector(".step.current");
const nextStep = step.dataset.id;
currentStep.classList.remove("current");
step.classList.add("current");
currentPaginate.textContent = "0" + nextStep;
let arrow;
if (currentStep.dataset.id < nextStep) {
arrow = document.querySelector(".arrow-bottom");
} else {
arrow = document.querySelector(".arrow-top");
}
if (update == true) updateArrows(arrow, currentStep, false);
}
}
I see what you mean.
Yes you are right. You can do it better...
Instead of passing the parameter arrow you can read from an event object
const changeStep = document.querySelectorAll(".step");
const currentPaginate = document.querySelector(".pagination span.active");
const arrows = document.querySelectorAll(".arrow");
for (let arrow of arrows) arrow.addEventListener("click", updateArrows);
for (let step of changeStep) step.addEventListener("click", next);
function updateArrows(e, currentStep = null, update = true) {
let arrow = null
e.target ? arrow=e.target : arrow=e
let nextStep;
if (currentStep == null) {
currentStep = document.querySelector(".step.current");
if (arrow.classList.contains("arrow-bottom")) {
nextStep = currentStep.nextElementSibling;
} else {
nextStep = currentStep.previousElementSibling;
}
} else nextStep = document.querySelector(".step.current");
if (!arrow.classList.contains("impossible")) {
if (nextStep.dataset.id != 1 && nextStep.dataset.id != 5) {
arrows.forEach(function (arrow) {
if (arrow.classList.contains("impossible")) {
arrow.classList.remove("impossible");
}
});
} else if (nextStep.dataset.id == 5) {
if (arrow.previousElementSibling.classList.contains("impossible"))
arrow.previousElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
} else if (nextStep.dataset.id == 1) {
if (arrow.nextElementSibling.classList.contains("impossible"))
arrow.nextElementSibling.classList.remove("impossible");
arrow.classList.add("impossible");
}
if (update == true) next(nextStep, false);
}
}
function next(e, update = true) {
let step = null
e.target ? step = e.target : step=e
if (!step.classList.contains("current")) {
const currentStep = document.querySelector(".step.current");
const nextStep = step.dataset.id;
currentStep.classList.remove("current");
step.classList.add("current");
currentPaginate.textContent = "0" + nextStep;
let arrow;
if (currentStep.dataset.id < nextStep) {
arrow = document.querySelector(".arrow-bottom");
} else {
arrow = document.querySelector(".arrow-top");
}
if (update == true) updateArrows(arrow, currentStep, false);
}
}
This should work, if not please contact me beforehand.
While activating the eventListener an event object is passed to function and e.target is an element which was clicked in this case.
What I did was crucial because you sometimes call this function from an eventListener and sometimes from a code. If the element has e.target then it's from an eventListener and if not then it's from code.
Didn't have a chance to test it since I don't have the rest of the code. Let me know if it works.

How to stop loop from executing again after it has already finished iterating?

I'm trying to build a calculator, and I used objects to store both the operator and the numbers put into the calculator. And then the loop in the evaluateOperation function takes the operator in the object along with the first value 'result' value and the second value and operates these values using the operate function.
The issue is, as soon as the loop is complete and exits, it loops again before exiting finally. Details can be found below, detailed comments on each part of the code.
const number_buttons = document.querySelectorAll('.number');
const input = document.getElementById('calc_display');
const op = document.querySelectorAll('.operator');
const multiplication = document.querySelector('#multiply');
const division = document.querySelector('#divide');
const addition = document.querySelector('#add');
const subtraction = document.querySelector('#subtract');
const evaluate = document.querySelector('#evaluate');
const decimal = document.querySelector('#decimal button');
const clearAllData = document.querySelector('.clear button')
//number storage
let num_display = {
result: 0,
};
let operatorStorage = {};
let count = 0;
let operatorSequence = 0;
let iterable_for_post_result = 1;
let iterable_for_result = 0;
let operatorType;
let operationResult = 0;
let clicked = 1;
let decimalClicks = 0;
let called = 1;
//performs operation
function operate(operator, num1, num2) {
switch (operator) {
case '+':
console.log(Number((add(num1, num2)).toFixed(2)));
return Number((add(num1, num2)).toFixed(2));
break;
case '-':
console.log(Number((subtract(num1, num2)).toFixed(2)));
return Number((subtract(num1, num2)).toFixed(2));
break;
case '*':
console.log(Number((multiply(num1, num2)).toFixed(2)));
return Number((multiply(num1, num2)).toFixed(2));
break;
case '/':
console.log(Number((divide(num1, num2)).toFixed(2)));
return Number((divide(num1, num2)).toFixed(2));
break;
}
}
// When numbers are clicked eventlistener
number_buttons.forEach(el => {
el.addEventListener('click', () => {
if (el.textContent == '.') {
console.log('deci');
if (decimalClicks == 1) {
el.disabled = true;
decimalClicks--;
} else {
decimalClicks++;
el.disabled = false;
input.value += el.textContent;
if (count == 0) {
// When an operation is called before a number is entered result becomes 0.
num_display.result = Number(input.value);
} else if (count >= 1) {
num_display[`post_op_result ${count}`] = Number(input.value);
}
}
} else {
input.value += el.textContent;
if (count == 0) {
num_display.result = Number(input.value);
} else if (count >= 1) {
num_display[`post_op_result ${count}`] = Number(input.value);
}
console.log(num_display);
}
});
});
// Eventlistener when the '=' is clicked
//
evaluate.addEventListener('click', () => {
iterable_for_result;
iterable_for_post_result;
// Condition for if user does not click an operator
if (operatorType == undefined || isNaN(operatorType) == true && Object.keys(num_display).length == 1) {
return;
} else {
// Condition for if user does not input the first number
if (input.value == '') {
// if a user inputted the first number but did not enter an operator
if (operatorType == undefined || isNaN(operatorType) == true) {
operatorType = '+';
}
input.value = `${num_display['result']}`;
operationResult = Number(input.value);
console.log(operationResult);
return operationResult;
} else {
let errorMessage = evaluateOperation(iterable_for_result, iterable_for_post_result);
// if divide number by 0
if (errorMessage == Infinity) {
console.log('Bwana');
errorMessage = 'YERRRRR';
input.value = `${errorMessage}`;
errorMessage = 0;
operationResult = Number(input.value);
num_display['result'] = operationResult;
} else {
//If no problems with inputs, calculates and calls evaluateOperation function
input.value = `${evaluateOperation(iterable_for_result, iterable_for_post_result)}`;
//operationResult = Number(input.value);
//num_display['result'] = operationResult;
}
}
}
iterable_for_post_result++;
iterable_for_result++;
console.log(num_display);
});
// operator click event listener
op.forEach(btn => {
btn.addEventListener('click', () => {
console.log(operatorType);
if (clicked > 1) {
clicked -= 2;
count++;
clear();
} else if (clicked <= 1) {
count++;
decimal.disabled = false;
decimalClicks = 0;
clicked++;
clear();
}
})
});
//Stores the operator clicked into operator object
function operatorTypeFunction(operator) {
operatorSequence++;
operatorStorage[`Operator ${operatorSequence}`] = operator;
operatorType = operatorStorage[`Operator ${operatorSequence}`];
console.log(operatorStorage);
}
// clears only the inputted value
function clear() {
input.value = "";
}
function add(num1, num2) {
return num1 + num2;
}
function subtract(num1, num2) {
return num1 - num2;
}
function multiply(num1, num2) {
return num1 * num2;
}
function divide(num1, num2) {
return num1 / num2;
}
// second inputted value
function storedKeys(i) {
return num_display[Object.keys(num_display)[i]];
}
// first inputted value/ also stored as the final result of every operation performed
// i.e. firstvalue = 10; secondvalue = 12;
// newvalue = firstvalue + secondvalue;
// Make firstvalue = 0 and then...
// firstvalue += newvalue
function resultNum1(i) {
if (operationResult == 0 && Object.keys(num_display).length <= 2) {
console.log('result ' + num_display[Object.keys(num_display)[i]]);
return num_display[Object.keys(num_display)[i]];
} else {
if (isNaN(operationResult) == true) {
operationResult = 0;
return operationResult;
} else {
return num_display['result'];
}
}
}
function evaluateOperation(iterable_variable_1, iterable_variable_2) {
let postResultIterable = 0;
let resultNumIterable = -1;
if (Object.keys(num_display).length > 2) {
console.log('Run');
// Something wrong with iteration, it loops through everything when all numbers are calculated
// it does another full loop with the new calculation after exiting the loop
for (let i = 1; i <= Object.keys(operatorStorage).length; i++) {
// The logging are to help to see the result and what is being used in the operate func
console.log(i);
resultNumIterable++;
postResultIterable++;
console.log(operatorStorage[`Operator ${i}`]);
console.log(storedKeys(postResultIterable));
operationResult = operate(operatorStorage[`Operator ${i}`], resultNum1(resultNumIterable), storedKeys(postResultIterable));
num_display['result'] = 0;
num_display['result'] += operationResult;
}
return num_display['result'];
} else {
return operate(operatorType, resultNum1(iterable_variable_1), storedKeys(iterable_variable_2));
}
}
//Clears all data stored and the input
clearAllData.addEventListener('click', () => {
for (let key in num_display) {
for (let i = count; i >= 0; i--) {
num_display['result'] = 0;
delete num_display[`post_op_result ${i}`];
delete operatorStorage[`Operator ${i}`];
}
}
clear();
operationResult = 0;
count = 0;
clicked = 1;
decimalClicks = 0;
iterable_for_post_result = 1;
iterable_for_result = 0;
operatorSequence = 0;
});
body {
display: flex;
justify-content: center;
align-items: center;
height: 80vh;
}
.grid_buttons {
width: 200px;
height: 200px;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(5, 1fr);
justify-content: center;
border: 1px solid black;
}
#calc_display {
grid-column: 1/4;
outline: none;
border: none;
border-bottom: 1px solid black;
}
#calc_display:hover {
outline: none;
}
.equal {
grid-column: 2/4;
}
.grid_buttons div>* {
width: 100%;
}
button {
padding: 5px;
font-size: 0.8em;
background-color: white;
border: none;
}
button:hover {
background-color: rgb(53, 157, 223);
color: white;
cursor: pointer;
}
.equal button {
background-color: rgb(53, 157, 223);
color: white;
}
<div class="grid_buttons">
<input type="text" id="calc_display" value="" readonly>
<div id="decimal"><button class="number">.</button></div>
<div><button class="operator" id="multiply" onclick="operatorTypeFunction('*');">*</button></div>
<div><button class="operator" id="divide" onclick="operatorTypeFunction('/')">/</button></div>
<div><button class="operator" id="add" onclick="operatorTypeFunction('+')">+</button></div>
<div><button class="operator" id="subtract" onclick="operatorTypeFunction('-')">-</button></div>
<div class="clear"><button>AC</button></div>
<div id="7"><button class="number">7</button></div>
<div id="8"><button class="number">8</button></div>
<div id="9"><button class="number">9</button></div>
<div id="4"><button class="number">4</button></div>
<div id="5"><button class="number">5</button></div>
<div id="6"><button class="number">6</button></div>
<div id="1"><button class="number">1</button></div>
<div id="2"><button class="number">2</button></div>
<div id="3"><button class="number">3</button></div>
<div id="0"><button class="number">0</button></div>
<div class="equal"><button id="evaluate">=</button></div>
</div>
It's not looping twice, you're calling evaluateOperation() twice. First in
let errorMessage = evaluateOperation(iterable_for_result, iterable_for_post_result);
and again in
input.value = `${evaluateOperation(iterable_for_result, iterable_for_post_result)}`;
Instead of calling it again, just use the value you assigned from the first call:
input.value = error_message;

Adding functionality for using the up and down arrow keys to select a table row

I need the help of an expert on my question below as it exceed and goes well beyond the level of knowledge that I have for programming in JavaScript.
Given the existing JavaScript coding below, how can I piggy back and add onto the existing coding so as to add functionality for a user to use their up and down arrow keys to scroll through the table, while they are scrolling through (btw the header column exempt) it would highlight the selected row and change its row color.
A point to note that if an existing table row is selected, and I hit my up or down arrow key, it would just move to and highlight the previous and next row. Some logic here is that I am guessing that one would need to find the row index to do this. Like I said, it is well beyond what I know how to do.
Much thanks and a huge appreciation for all your help.
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
tr.normal td {
color: black;
background-color: white;
}
tr.highlighted td {
color: white;
background-color: red;
}
</style>
</head>
<body>
<div id="results" class="scrollingdatagrid">
<table id="mstrTable" cellspacing="0" border="1">
<thead>
<tr>
<th>File Number</th>
<th>Date1</th>
<th>Date2</th>
<th>Status</th>
<th>Num.</th>
</tr>
</thead>
<tbody>
<tr>
<td>KABC</td>
<td>09/12/2002</td>
<td>09/12/2002</td>
<td>Submitted</td>
<td>1</td>
</tr>
<tr>
<td>KCBS</td>
<td>09/11/2002</td>
<td>09/11/2002</td>
<td>Lockdown</td>
<td>2</td>
</tr>
<tr>
<td>WFLA</td>
<td>09/11/2002</td>
<td>09/11/2002</td>
<td>Submitted</td>
<td>3</td>
</tr>
<tr>
<td>WTSP</td>
<td>09/15/2002</td>
<td>09/15/2002</td>
<td>In-Progress</td>
<td>4</td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript">
(
function() {
var trows = document.getElementById("mstrTable").rows;
for (var t = 1; t < trows.length; ++t) {
trow = trows[t];
trow.className = "normal";
trow.onclick = highlightRow;
}//end for
function highlightRow() {
for ( var t = 1; t < trows.length; ++t ) {
trow = trows[t];
if (trow != this) { trow.className = "normal" }
}//end for
this.className = (this.className == "highlighted")?"normal":"highlighted";
}//end function
}//end function
)();//end script
</script>
</body>
</html>
This is certainly not optimal, but as you're not using jQuery (or a similar library) you've incurred a lot of cross browser overhead. This should be backwards compatible up to IE8.
Live Demo
HTML
Only change here is the addition of the tabindex
<table tabindex='0' id="mstrTable" cellspacing="0" border="1">
JS
//From: http://forrst.com/posts/JavaScript_Cross_Browser_Event_Binding-yMd
var addEvent = (function( window, document ) {
if ( document.addEventListener ) {
return function( elem, type, cb ) {
if ( (elem && !elem.length) || elem === window ) {
elem.addEventListener(type, cb, false );
}
else if ( elem && elem.length ) {
var len = elem.length;
for ( var i = 0; i < len; i++ ) {
addEvent( elem[i], type, cb );
}
}
};
}
else if ( document.attachEvent ) {
return function ( elem, type, cb ) {
if ( (elem && !elem.length) || elem === window ) {
elem.attachEvent( 'on' + type, function() { return cb.call(elem, window.event) } );
}
else if ( elem.length ) {
var len = elem.length;
for ( var i = 0; i < len; i++ ) {
addEvent( elem[i], type, cb );
}
}
};
}
})( this, document );
//derived from: http://stackoverflow.com/a/10924150/402706
function getpreviousSibling(element) {
var p = element;
do p = p.previousSibling;
while (p && p.nodeType != 1);
return p;
}
//derived from: http://stackoverflow.com/a/10924150/402706
function getnextSibling(element) {
var p = element;
do p = p.nextSibling;
while (p && p.nodeType != 1);
return p;
}
;(function() {
var trows = document.getElementById("mstrTable").rows;
for (var t = 1; t < trows.length; ++t) {
trow = trows[t];
trow.className = "normal";
trow.onclick = highlightRow;
}//end for
function highlightRow() {
for ( var t = 1; t < trows.length; ++t ) {
trow = trows[t];
if (trow != this) { trow.className = "normal" }
}//end for
this.className = (this.className == "highlighted")?"normal":"highlighted";
}//end function
addEvent(document.getElementById('mstrTable'), 'keydown', function(e){
var key = e.keyCode || e.which;
if((key === 38 || key === 40) && !e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey){
var highlightedRows = document.querySelectorAll('.highlighted');
if(highlightedRows.length > 0){
var highlightedRow = highlightedRows[0];
var prev = getpreviousSibling(highlightedRow);
var next = getnextSibling(highlightedRow);
if(key === 38 && prev && prev.nodeName === highlightedRow.nodeName){//up
highlightedRow.className = 'normal';
prev.className = 'highlighted';
} else if(key === 40 && next && next.nodeName === highlightedRow.nodeName){ //down
highlightedRow.className = 'normal';
next.className = 'highlighted';
}
}
}
});
})();//end script
I don't think this actually needs to be that long--you just need to keep the index of the currently highlighted row.
This has only been tested on Chrome (I don't have IE), but it should work.
(function() {
/**
* Gets the tr at the specified row or column
*/
var tbody = document.getElementsByTagName('tbody')[0];
function getRow(row) {
return tbody.getElementsByTagName('tr')[row];
}
// store these so we won't have to keep recalculating
var numRows = tbody.getElementsByTagName('tr').length;
// index of the currently highlighted row
var curRow = 0;
// highlight the initially highlighted cell
getRow(curRow).className = 'highlighted';
// listen for keydown event
if (addEventListener) {
window.addEventListener('keydown',keydownHandler, false);
} else if (window.attachEvent) {
window.attachEvent('onkeydown', keydownHandler);
}
// handle keydown event
function keydownHandler (evt) {
// return the old cell to normal
getRow(curRow).className = 'normal';
// increment/decrement the position of the current cell
// depending on the key pressed
if (evt.keyCode == 38 && curRow > 0) // up
curRow--;
else if (evt.keyCode == 40 && curRow < numRows-1) // down
curRow++;
// update the new cell
getRow(curRow).className = 'highlighted';
}
})();//end script
I have create a demo using JQuery here on JSBin
In general, we have 2 task:
highlight selected row
choose next/prev row
To highlight the "clicked" row, I use this code
$("#mstrTable tr").click(function(evt){
var element = $(evt.target);
var tableElement = element.parents('table');
tableElement.find('tr').removeClass('highlighted');
element.parents('tr').addClass('highlighted');
});
To choose next/prev row, I use jQuery tree traversal function with some exception when there is no tr inside your tbody. Note that keyCode of left, right, up, down arrow are 37, 39, 38, 40 respectively.
$(document).keypress(function(evt){
var highlightedRow = $("#mstrTable .highlighted");
if (highlightedRow.length > 0) // table cell is selected
{
var tbodyElement = highlightedRow.parents('tbody');
var trElements = tbodyElement.find('tr');
var nextElement = highlightedRow.next('tr');
var prevElement = highlightedRow.prev('tr');
trElements.removeClass("highlighted");
switch(evt.keyCode)
{
case 40:
if(nextElement.length)
{
nextElement.addClass('highlighted');
}
else if (trElements.length)
{
$(trElements[0]).addClass('highlighted');
}
break;
case 38:
if(prevElement.length)
{
prevElement.addClass('highlighted');
}
else if (trElements.length)
{
$(trElements[trElements.length - 1]).addClass('highlighted');
}
break;
}
}
});
Here is the complete solution which selects rows in table just like a windows file selection works.
add class multiSelect to you table and then place this code in a JS file
$(document).ready(function() {
var selectionPivot;
// code for selected rows.
$('.multiSelect tbody').on( 'click', 'tr', function (e) {
var tbodyElement = $(this).parents('tbody');
var trElements = tbodyElement.find('tr');
if(!e.ctrlKey && (!e.shiftKey) ){
trElements.removeClass("row_selected");
selectionPivot=$(this);
}
if(e.shiftKey){
var bot = Math.min(selectionPivot[0].rowIndex, $(this)[0].rowIndex);
var top = Math.max(selectionPivot[0].rowIndex, $(this)[0].rowIndex);
trElements.removeClass("row_selected");
for(var i=bot; i<=top; i++){
trElements[i-1].className+=" row_selected";
}
}
else {
selectionPivot=$(this);
trElements.removeClass("focus");
$(this).addClass('focus');
if ( $(this).hasClass('row_selected') ) {
$(this).removeClass('row_selected');
}
else {
$(this).addClass('row_selected');
}
}
});
$(document).keypress(function(evt){
if(evt.shiftKey){
var highlightedRow = $(".multiSelect .focus");
if (highlightedRow.length > 0) // table cell is selected
{
var tbodyElement = highlightedRow.parents('tbody');
var trElements = tbodyElement.find('tr');
var nextElement = highlightedRow.next('tr');
var prevElement = highlightedRow.prev('tr');
trElements.removeClass("focus");
switch(evt.keyCode)
{
case 40:
if(nextElement.length)
{
nextElement.addClass('row_selected');
nextElement.addClass('focus');
}
else if (trElements.length)
{
$(trElements[0]).addClass('row_selected');
$(trElements[0]).addClass('focus');
}
break;
case 38:
if(prevElement.length)
{
prevElement.addClass('row_selected');
prevElement.addClass('focus');
}
else if (trElements.length)
{
$(trElements[trElements.length - 1]).addClass('row_selected');
$(trElements[trElements.length - 1]).addClass('focus');
}
break;
}
}
}
});
});

Passing string to javascript function results in value

Something really odd going on here, and I have gone round in circles trying to figure out what is going on...I have a couple of input boxes, with onchange events firing for them, the event being loaded with a JS function that takes the value ( name of another item ) and actions the function accordingly. Only thing is, when the value of the string arrives at the other function, it has somehow been assigned a numeric value, specifically that of the input box.
My php that helps build the form:
$filterfield = '"p_delweek"';
print "<span class='filter'>Del Week<input class='menulink spin-button' id='weekno' type='text' value='".$weekno."' onKeyUp='doFilter($filterfield)' onChange='doFilter($filterfield)' data-filtered='0'/><input type='button' value='Clear' onClick='doUnfilter()'></span>";
$filterfield = '"p_seedweek"';
print "<span class='filter'>Sow Week<input class='menulink spin-button' id='sowweekno' type='text' value='".$weekno."' onKeyUp='doFilter($filterfield)' onChange='doFilter($filterfield)' data-filtered='0'/><input type='button' value='Clear' onClick='doUnfilter()'></span>";
Resulting HTML in source:
<span class="filter">Del Week<input style="width: 50px; height: 22px;" class="menulink spin-button smartspinner" id="weekno" value="26" onkeyup='doFilter("p_delweek")' onchange='doFilter("p_delweek")' data-filtered="0" type="text"><input value="Clear" onclick="doUnfilter()" type="button"></span><span class="filter">Sow Week<input style="width: 50px; height: 22px;" class="menulink spin-button smartspinner" id="sowweekno" value="26" onkeyup='doFilter("p_seedweek")' onchange='doFilter("p_seedweek")' data-filtered="0" type="text"><input value="Clear" onclick="doUnfilter()" type="button"></span>
Javascript function that is called:
function doFilter(filterfield) {
console.log("DoFilter:"+filterfield);
var filterInfo=[
{
fieldName : filterfield,
logic : "equal",
value : Sigma.Util.getValue("weekno")
}
]
// the next lines action the filtering
var grid=Sigma.$grid("myGrid1");
console.log("filterinfo="+filterInfo);
var rowNOs=grid.applyFilter(filterInfo);
}
It all goes fine until we get to the console.log("DoFilter:"+filterfield) , which results in DoFilter:25; 25 happens to be the value of the input box.
How is it grabbing that value? How to pass the real one?
TBH — I'm not sure if I got what you're after. However, if you must call a function inline (I recommend that you don’t), you can pass a reference to the input field as parameter and make it available in the methods’s body:
<input onchange="doFilter('p_delweek', this)" type="text">
​
function doFilter(filterfield, field) {
console.log(filterfield);
// field is a reference to the input field, hence
console.log(field.value);
// will print the current value for this field
}
This is not the answer, this file is the problem:
(function($) {
$.fn.extend({
spinit: function(options) {
var settings = $.extend({ min: 0, max: 100, initValue: 0, callback: doFilter, stepInc: 1, pageInc: 10, width: 50, height: 15, btnWidth: 10, mask: '' }, options);
return this.each(function() {
var UP = 38;
var DOWN = 40;
var PAGEUP = 33;
var PAGEDOWN = 34;
var mouseCaptured = false;
var mouseIn = false;
var interval;
var direction = 'none';
var isPgeInc = false;
var value = Math.max(settings.initValue, settings.min);
var el = $(this).val(value).css('width', (settings.width) + 'px').css('height', settings.height + 'px').addClass('smartspinner');
raiseCallback(value);
if (settings.mask != '') el.val(settings.mask);
$.fn.reset = function(val) {
if (isNaN(val)) val = 0;
value = Math.max(val, settings.min);
$(this).val(value);
raiseCallback(value);
};
function setDirection(dir) {
direction = dir;
isPgeInc = false;
switch (dir) {
case 'up':
setClass('up');
break;
case 'down':
setClass('down');
break;
case 'pup':
isPgeInc = true;
setClass('up');
break;
case 'pdown':
isPgeInc = true;
setClass('down');
break;
case 'none':
setClass('');
break;
}
}
el.focusin(function() {
el.val(value);
});
el.click(function(e) {
mouseCaptured = true;
isPgeInc = false;
clearInterval(interval);
onValueChange();
});
el.mouseenter(function(e) {
el.val(value);
});
el.mousemove(function(e) {
if (e.pageX > (el.offset().left + settings.width) - settings.btnWidth - 4) {
if (e.pageY < el.offset().top + settings.height / 2)
setDirection('up');
else
setDirection('down');
}
else
setDirection('none');
});
el.mousedown(function(e) {
isPgeInc = false;
clearInterval(interval);
interval = setTimeout(onValueChange, 250);
});
el.mouseup(function(e) {
mouseCaptured = false;
isPgeInc = false;
clearInterval(interval);
});
el.mouseleave(function(e) {
setDirection('none');
if (settings.mask != '') el.val(settings.mask);
}); el.keydown(function(e) {
switch (e.which) {
case UP:
setDirection('up');
onValueChange();
break; // Arrow Up
case DOWN:
setDirection('down');
onValueChange();
break; // Arrow Down
case PAGEUP:
setDirection('pup');
onValueChange();
break; // Page Up
case PAGEDOWN:
setDirection('pdown');
onValueChange();
break; // Page Down
default:
setDirection('none');
break;
}
});
el.keyup(function(e) {
setDirection('none');
});
el.keypress(function(e) {
if (el.val() == settings.mask) el.val(value);
var sText = getSelectedText();
if (sText != '') {
sText = el.val().replace(sText, '');
el.val(sText);
}
if (e.which >= 48 && e.which <= 57) {
var temp = parseFloat(el.val() + (e.which - 48));
if (temp >= settings.min && temp <= settings.max) {
value = temp;
raiseCallback(value);
}
else {
e.preventDefault();
}
}
});
el.blur(function() {
if (settings.mask == '') {
if (el.val() == '')
el.val(settings.min);
}
else {
el.val(settings.mask);
}
});
el.bind("mousewheel", function(e) {
if (e.wheelDelta >= 120) {
setDirection('down');
onValueChange();
}
else if (e.wheelDelta <= -120) {
setDirection('up');
onValueChange();
}
e.preventDefault();
});
if (this.addEventListener) {
this.addEventListener('DOMMouseScroll', function(e) {
if (e.detail > 0) {
setDirection('down');
onValueChange();
}
else if (e.detail < 0) {
setDirection('up');
onValueChange();
}
e.preventDefault();
}, false);
}
function raiseCallback(val) {
if (settings.callback != null) settings.callback(val);
}
function getSelectedText() {
var startPos = el.get(0).selectionStart;
var endPos = el.get(0).selectionEnd;
var doc = document.selection;
if (doc && doc.createRange().text.length != 0) {
return doc.createRange().text;
} else if (!doc && el.val().substring(startPos, endPos).length != 0) {
return el.val().substring(startPos, endPos);
}
return '';
}
function setValue(a, b) {
if (a >= settings.min && a <= settings.max) {
value = b;
} el.val(value);
}
function onValueChange() {
if (direction == 'up') {
value += settings.stepInc;
if (value > settings.max) value = settings.max;
setValue(parseFloat(el.val()), value);
}
if (direction == 'down') {
value -= settings.stepInc;
if (value < settings.min) value = settings.min;
setValue(parseFloat(el.val()), value);
}
if (direction == 'pup') {
value += settings.pageInc;
if (value > settings.max) value = settings.max;
setValue(parseFloat(el.val()), value);
}
if (direction == 'pdown') {
value -= settings.pageInc;
if (value < settings.min) value = settings.min;
setValue(parseFloat(el.val()), value);
}
raiseCallback(value);
}
function setClass(name) {
el.removeClass('up').removeClass('down');
if (name != '') el.addClass(name);
}
});
}
});
})(jQuery);
Why and where does this alter the passing value of a function attached to the < INPUT > ?

Categories

Resources