How to fix function scope issues when calling a function - javascript

I'm studying a project by Wes Bos where you were tasked with adding shift click functionality to a series of list items.
I completed this and wanted to go further by then adding the ability to deselect these list items which I did (see commented javascript code).
Then I wanted to take that solution and apply DRY principles and that's where things became difficult.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hold Shift to Check Multiple Checkboxes</title>
</head>
<body>
<style>
html {
font-family: sans-serif;
background: #ffc600;
}
.inbox {
max-width: 400px;
margin: 50px auto;
background: white;
border-radius: 5px;
box-shadow: 10px 10px 0 rgba(0,0,0,0.1);
}
.item {
display: flex;
align-items: center;
border-bottom: 1px solid #F1F1F1;
}
.item:last-child {
border-bottom: 0;
}
input:checked + p {
background: #F9F9F9;
text-decoration: line-through;
}
input[type="checkbox"] {
margin: 20px;
}
input:hover {
cursor: pointer;
}
p {
margin: 0;
padding: 20px;
transition: background 0.2s;
flex: 1;
font-family:'helvetica neue';
font-size: 20px;
font-weight: 200;
border-left: 1px solid #D1E2FF;
}
</style>
<!--
The following is a common layout you would see in an email client.
When a user clicks a checkbox, holds Shift, and then clicks another checkbox a few rows down, all the checkboxes inbetween those two checkboxes should be checked.
-->
<div class="inbox">
<div class="item">
<input type="checkbox">
<p>This is an inbox layout.</p>
</div>
<div class="item">
<input type="checkbox">
<p>Check one item</p>
</div>
<div class="item">
<input type="checkbox">
<p>Hold down your Shift key</p>
</div>
<div class="item">
<input type="checkbox">
<p>Check a lower item</p>
</div>
<div class="item">
<input type="checkbox">
<p>Everything in between should also be set to checked</p>
</div>
<div class="item">
<input type="checkbox">
<p>Try to do it without any libraries</p>
</div>
<div class="item">
<input type="checkbox">
<p>Just regular JavaScript</p>
</div>
<div class="item">
<input type="checkbox">
<p>Good Luck!</p>
</div>
<div class="item">
<input type="checkbox">
<p>Don't forget to tweet your result!</p>
</div>
</div>
<script>
const input = document.querySelectorAll('.item input[type="checkbox"]');
let lastchecked;
function checkFunction (event) {
let inbetween = false;
if (event.shiftKey && this.checked) {
tickBox(true);
} else if (event.shiftKey && !this.checked) {
tickBox(false);
}
lastChecked = this;
}
function tickBox(boolean) {
input.forEach(box => {
if (box === this || box === lastChecked) {
inbetween = !inbetween;
}
if (inbetween) {
box.checked = boolean;
}
});
}
input.forEach(box => box.addEventListener('click', checkFunction));
// const input = document.querySelectorAll('.item input[type="checkbox"]');
// let lastchecked;
//
// function checkFunction (event) {
// let inbetween = false;
// if (event.shiftKey && this.checked) {
// input.forEach(box => {
// if (box === this || box === lastChecked) {
// inbetween = !inbetween;
// }
// if (inbetween) {
// box.checked = true;
// }
// });
// } else if (event.shiftKey && !this.checked) {
// input.forEach(box => {
// if (box === this || box === lastChecked) {
// inbetween = !inbetween;
// }
// if (inbetween) {
// box.checked = false;
// }
// });
// }
// lastChecked = this;
// }
//
// input.forEach(box => box.addEventListener('click', checkFunction));
</script>
</body>
</html>
I expected at that point that all I had to do was call the function where the code was repeated, but this time using a boolean parameter, however it then says that the inbetween variable is undefined. I'm confused at this point. If I define it within the new function it just ticks everything and doesn't change the variable back to false etc.
I hope that makes sense. I'd love to know where I'm going wrong for the future.
Thanks all.

I'd suggest declaring tickBox() inside of checkFunction(). This is feasible because it's never called elsewhere. Then, it can have access to the scope of checkFunction() including the inbetween variable:
const input = document.querySelectorAll('.item input[type="checkbox"]');
let lastchecked;
function checkFunction (event) {
let inbetween = false;
function tickBox(boolean) {
input.forEach(box => {
if (box === this || box === lastChecked) {
inbetween = !inbetween;
}
if (inbetween) {
box.checked = boolean;
}
});
}
if (event.shiftKey && this.checked) {
tickBox(true);
} else if (event.shiftKey && !this.checked) {
tickBox(false);
}
lastChecked = this;
}
input.forEach(box => box.addEventListener('click', checkFunction));
FYI, you can simplify your if/else from this:
if (event.shiftKey && this.checked) {
tickBox(true);
} else if (event.shiftKey && !this.checked) {
tickBox(false);
}
to this:
if (event.shiftKey) {
tickBox(this.checked);
}
Which then means that you don't actually need a separate function for tickBox() any more.

Related

Issues with JavaScript list [duplicate]

This question already has answers here:
Clicking a button within a form causes page refresh
(11 answers)
Is there a way to add/remove several classes in one single instruction with classList?
(16 answers)
Closed 8 months ago.
I am assigned to create this to do list using eventlisteners and using JavaScript. My HTML and CSS are given to me however I believe I may be confusing my Id's with each other. The expectation is that a new item is added to the list, can be deleted from the list when clicked on the trashcan, and the input is cleared. Any advice on what I am missing would be helpful... I've been staring at this for 7hrs now.
const todoObjectList = [];
class toDo_Class {
constructor(item) {
this.ulElement = item;
}
add() {
const todoInput = document.querySelector("#todo-input").value;
if (todoInput == "") {
alert("Nothing was entered!");
} else {
const todoObject = {
id: todoObjectList.length,
todoText: todoInput,
isDone: false,
};
todoObjectList.unshift(todoObject);
this.display();
document.querySelector("#todo-input").value = '';
}
}
done_undone(x) {
const selectedTodoIndex = todoObjectList.findIndex((item) => item.id == x);
console.log(todoObjectList[selectedTodoIndex].isDone);
todoObjectList[selectedTodoIndex].isDone == false ? todoObjectList[selectedTodoIndex].isDone == true : todoObjectList[selectedTodoIndex].isDone = false;
this.display();
}
deleteElement(z) {
const selectedDelIndex = todoObjectList.findIndex((item) => item.id == z);
todoObjectList.splice(selectedDelIndex, 1);
this.display();
}
display() {
this.ulElement.innerHTML = "";
todoObjectList.forEach((object_item) => {
const liElement = document.createElement("li");
const delBtn = document.createElement("i");
liElement.innerText = object_item.todoText;
liElement.setAttribute("data-id", object_item.id);
delBtn.setAttribute("data-id", object_item.id);
delBtn.classList.add("fas fa-trash-alt");
liElement.appendChild(delBtn);
delBtn.addEventListener("click", function(e) {
const deleteId = e.target.getAttribute("data-id");
toDoList.deleteElement(deleteId);
});
liElement.addEventListener("click", function(e) {
const selectedId = e.target.getAttribute("data-id");
toDoList.done_undone(selectedId);
});
if (object_item.isDone) {
liElement.classList.add("checked");
}
this.ulElement.appendChild(liElement);
});
}
}
const listSection = document.querySelector("#todo-ul");
toDoList = new toDo_Class(listSection);
document.querySelector("#todo-btn").addEventListener("click", function() {
toDoList.add();
});
document.querySelector("#todo-input").addEventListener("keydown", function(e) {
if (e.keyCode == 13) {
toDoList.add();
}
});
body {
background-color: #34495e;
font-family: "Lato", sans-serif;
}
button {
margin: 0 auto;
float: right;
}
.centered {
margin: 0 auto;
width: 80%;
}
.card {
margin: 50px auto;
width: 18rem;
}
i {
float: right;
padding-top: 5px;
}
<html lang="en">
<body>
<div class="card">
<div class="card-body">
<h3 class="card-title">Today's To Do List</h3>
<form id="todo-form">
<div class="form-group">
<input type="text" class="form-control" id="todo-input" placeholder="What else do you need to do?">
</div>
<div class="form-group">
<input type="submit" id="todo-btn" class="btn btn-secondary btn-block" value="Add Item To List">
</div>
</form>
</div>
<ul class="list-group list-group-flush" id="todo-ul">
<li class="list-group-item">Pick up groceries <i class="fas fa-trash-alt"></i>
</li>
</ul>
</div>
</body>
</html>

How to use an inner tag by class or id to drag and drop an entire element in pure js?

I have example of drag and drop: https://codepen.io/ilq-trifonow/pen/GRmaoBP
Now I can move elements by any part of them. But I want to use a <p> tag with a class="drag-handler" for this. How can I do it?
index.html
<html>
<head>
<meta charset="utf-8">
<title>Список задач с drag & drop</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="tasks">
<h1 class="tasks__title">To do list</h1>
<div class="tasks__list">
<div class="tasks__item"><p class="drag-handler">Drag it</p><p>learn HTML</p></div>
<div class="tasks__item"><p class="drag-handler">Drag it</p><p>learn CSS</p></div>
<div class="tasks__item"><p class="drag-handler">Drag it</p><p>learn JavaScript</p></div>
<div class="tasks__item"><p class="drag-handler">Drag it</p><p>learn PHP</p></div>
<div class="tasks__item"><p class="drag-handler">Drag it</p><p>stay alive</p></div>
</div>
</div>
</body>
</html>
index.js
const tasksListElement = document.querySelector(`.tasks__list`);
const taskElements = tasksListElement.querySelectorAll(`.tasks__item`);
for (const task of taskElements) {
task.draggable = true;
}
tasksListElement.addEventListener(`dragstart`, (evt) => {
evt.target.classList.add(`selected`);
});
tasksListElement.addEventListener(`dragend`, (evt) => {
evt.target.classList.remove(`selected`);
});
const getNextElement = (cursorPosition, currentElement) => {
const currentElementCoord = currentElement.getBoundingClientRect();
const currentElementCenter = currentElementCoord.x + currentElementCoord.width / 2;
const nextElement = (cursorPosition < currentElementCenter) ?
currentElement :
currentElement.nextElementSibling;
return nextElement;
};
tasksListElement.addEventListener(`dragover`, (evt) => {
evt.preventDefault();
const activeElement = tasksListElement.querySelector(`.selected`);
const currentElement = evt.target;
const isMoveable = activeElement !== currentElement &&
currentElement.classList.contains(`tasks__item`);
if (!isMoveable) {
return;
}
const nextElement = getNextElement(evt.clientX, currentElement);
if (
nextElement &&
activeElement === nextElement.previousElementSibling ||
activeElement === nextElement
) {
return;
}
tasksListElement.insertBefore(activeElement, nextElement);
});
You can set draggable=true for all inner tag (say p.drag-handler) and then in dragover listener use parentElement as a currentElement that must be dragged.
Here is working sample:
const tasksListElement = document.querySelector(`.tasks__list`);
const taskElements = tasksListElement.querySelectorAll(`.drag-handler`);
for (const task of taskElements) {
task.draggable = true;
}
tasksListElement.addEventListener(`dragstart`, (evt) => {
evt.target.parentElement.classList.add(`selected`);
});
tasksListElement.addEventListener(`dragend`, (evt) => {
evt.target.parentElement.classList.remove(`selected`);
});
const getNextElement = (cursorPosition, currentElement) => {
const currentElementCoord = currentElement.getBoundingClientRect();
const currentElementCenter = currentElementCoord.x + currentElementCoord.width / 2;
const nextElement = (cursorPosition < currentElementCenter) ?
currentElement :
currentElement.nextElementSibling;
return nextElement;
};
tasksListElement.addEventListener(`dragover`, (evt) => {
evt.preventDefault();
const activeElement = tasksListElement.querySelector(`.selected`);
const currentElement = evt.target.parentElement;
const isMoveable = activeElement !== currentElement &&
currentElement.classList.contains(`tasks__item`);
if (!isMoveable) {
return;
}
const nextElement = getNextElement(evt.clientX, currentElement);
tasksListElement.insertBefore(activeElement, nextElement);
});
html, body {
margin: 0;
padding: 0;
}
body {
font-family: "Tahoma", sans-serif;
font-size: 18px;
line-height: 25px;
color: #164a44;
background-color: #b2d9d0;
}
.tasks__title {
margin: 50px 0 20px 0;
text-align: center;
text-transform: uppercase;
}
.tasks__list {
display: flex;
justify-content: center;
}
.tasks__item {
width: 250px;
margin-right: 10px;
padding: 5px;
text-align: center;
border: 2px dashed #b2d9d0;
border-radius: 10px;
background-color: #dff2ef;
transition: background-color 0.5s;
}
.tasks__item:last-child {
margin-bottom: 0;
}
.selected {
opacity: 0.6;
}
.drag-handler {
background-color: #ddd;
cursor: move;
}
<div class="tasks">
<h1 class="tasks__title">To do list</h1>
<div class="tasks__list">
<div class="tasks__item" ><p class="drag-handler">Drag it</p><p>learn HTML</p></div>
<div class="tasks__item" ><p class="drag-handler">Drag it</p><p>learn CSS</p></div>
<div class="tasks__item" ><p class="drag-handler">Drag it</p><p>learn JavaScript</p></div>
<div class="tasks__item" ><p class="drag-handler">Drag it</p><p>learn PHP</p></div>
<div class="tasks__item" ><p class="drag-handler">Drag it</p><p>stay alive</p></div>
</div>
</div>
If I understand you correctly, you want to move the element by selecting inside of the paragraph element to move the entire containing div element.
In the scope of what you have written, I would simply use an implicit reference inside of the child node to set the draggable element when you select it
for (const task of taskElements) {
task.firstChild.addEventListener(`mousedown`, (evt) => {
task.draggable = true
task.classList.add(`selected`)
})
}
Further to this, I wouldn't rely on DOM reads / writes as it tends to be more expensive than simply holding onto a reference to the element you are looking for:
let activeElement
for (const task of taskElements) {
task.firstChild.addEventListener(`mousedown`, (evt) => {
task.draggable = true
activeElement = task
})
}
...
tasksListElement.addEventListener(`dragover`, (evt) => {
evt.preventDefault();
const currentElement = evt.target;
const isMoveable = activeElement !== currentElement &&
currentElement.classList.contains(`tasks__item`);
if (!isMoveable) {
return;
}
const nextElement = getNextElement(evt.clientX, currentElement);
if (
nextElement &&
activeElement === nextElement.previousElementSibling ||
activeElement === nextElement
) {
return;
}
tasksListElement.insertBefore(activeElement, nextElement);
});
I gave this a try in your codepen, should be fine.

Need advice (mentoring)

Created a validation form in vanilla JS. Due to my little experience, I have a bunch of "if" here, I can not transfer the coordinates of Yandex.Maps to another function for validation, and it is still unknown what. With the coordinates, I "crutch" by saving them to an invisible div :-) I deleted my Api key, so I won't be able to get the coordinates in the example, I don't know if it's worth giving the key to public access :-) The code is working.
Tell me how you can reduce the code or how to do button validation without my crutch with a div :-)
let errorName = document.querySelector(".name-error");
let errorNumber = document.querySelector(".number-error");
let errorEmail = document.querySelector(".email-error");
let errorMap = document.querySelector(".map-error");
let completed = document.querySelector(".mailForm__completed");
function viodChecker() {
let name = document.querySelector(".mailForm__name").value;
let number = document.querySelector(".mailForm__number").value;
let Email = document.querySelector(".mailForm__email").value;
let Coords = document.querySelector(".coords");
if (name === "") {
errorName.style.display = "block";
}
if (name !== "") {
errorName.style.display = "none";
}
if (number === "") {
errorNumber.style.display = "block";
}
if (number !== "") {
errorNumber.style.display = "none";
}
if (Email.indexOf("#") === -1) {
errorEmail.style.display = "block";
}
if (Email.indexOf("#") !== -1) {
errorEmail.style.display = "none";
}
if (Email.indexOf("#") !== -1) {
errorEmail.style.display = "none";
}
if (Coords.textContent === "") {
errorMap.style.display = "block";
}
if (Coords.textContent !== "") {
errorMap.style.display = "none";
}
if (name !== "" && number !== "" && Email.indexOf("#") !== -1 && Coords.textContent !== "") {
completed.style.display = "block";
}
}
ymaps.ready(init);
function init() {
var myPlacemark,
myMap = new ymaps.Map('map', {
center: [40.391917, -73.524590],
zoom: 17,
controls: ['geolocationControl', 'searchControl']
});
// We listen to the click on the map.
myMap.events.add('click', function (e) {
var coords = e.get('coords');
// If the label has already been created, just move it.
if (myPlacemark) {
myPlacemark.geometry.setCoordinates(coords);
}
// If not, we create.
else {
myPlacemark = createPlacemark(coords);
myMap.geoObjects.add(myPlacemark);
// Listen to the drag-and-drop event on the label.
myPlacemark.events.add('dragend', function () {
getAddress(myPlacemark.geometry.getCoordinates());
});
}
getAddress(coords);
});
// Create a label.
function createPlacemark(coords) {
return new ymaps.Placemark(coords, {
iconCaption: 'search...'
}, {
preset: 'islands#violetDotIconWithCaption',
draggable: true
});
}
// Coords
function getAddress(coords) {
myPlacemark.properties.set('iconCaption', 'поиск...');
ymaps.geocode(coords).then(function (res) {
var firstGeoObject = res.geoObjects.get(0);
myPlacemark.properties
.set({
// We form a line with data about the object.
iconCaption: [
firstGeoObject.geometry.getCoordinates(),
].filter(Boolean).join(', '),
// We set the line with the object address as the balloon content.
balloonContent: firstGeoObject
});
document.querySelector(".coords").textContent = firstGeoObject.geometry.getCoordinates();
});
}
}
body, .mailForm {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
input, textarea {
font-size: 14px;
margin: 10px 0 0 0;
width: 350px;
resize: none;
}
.mailForm__map {
margin-top: 10px;
}
button {
margin: 10px 0 0 0;
font-size: 14px;
cursor: pointer;
}
.mailForm__comment {
display: inline-block;
height: 200px;
}
.mailForm__error, .mailForm__completed, .coords {
display: none;
color: red;
font-size: 16px;
font-weight: bold;
margin-top: 10px;
}
.mailForm__completed {
color: green;
font-size: 20px;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="style.css" rel="stylesheet">
<title>Mail Form</title>
<script src="https://api-maps.yandex.ru/2.1/?apikey=5200498b-0123-4796-1-bee4ea5473212&lang=ru_RU" type="text/javascript">
</script>
</head>
<body>
<div class="mailForm">
<input class="mailForm__name" placeholder="Name" type="text" value="">
<input class="mailForm__number" placeholder="Tel" type="tel" value="">
<input class="mailForm__email" placeholder="Email" type="email" value="">
<div id="map" class="mailForm__map" style="width: 100%; height: 200px">
</div>
<textarea class="mailForm__comment" maxlength="500" placeholder="Comment" type="text" value=""></textarea>
<button onclick="viodChecker()">SEND</button>
<div class="mailForm__error name-error">NAME ERROR</div>
<div class="mailForm__error number-error">TEL ERROR</div>
<div class="mailForm__error email-error"># ERROR</div>
<div class="mailForm__error map-error">COORDS ERROR</div>
<div class="mailForm__completed">NICE!</div>
<div class="coords"></div>
</div>
<script src="script.js"></script>
</body>
</html>

Javascript simple function for selecting element and deselecting all others

I have a simple function that is supposed to check if an element has a class. If it doesnt have the class, it should add the class. It should also iterate through all other elements in the series, and deselect them. So only 1 element should be shown at any time. I have looked at this, this, this and this as well as a bunch of others. I know the function should be simple, but I just cant resolve it.
const changeFilter = (itemNumber) => {
// grab element that has been clicked
let selector = document.getElementsByClassName(itemNumber);
// check if element doesnt has the class name that underlines it
if (!selector[0].classList.contains("darken-filter-selector")) {
// add the class if it doesnt have it
selector[0].classList.add("darken-filter-selector")
} else {
// iterate through all the elements, check if it has the class
// remove it if it doesnt have the class
for (let i = 0; i < 7 ; i++) {
if(i !== itemNumber) {
return selector[i].classList.contains("darken-filter-selector") ? selector[0].classList.remove("darken-filter-selector"):null
}
}
}
};
And it should not look like this (what is currently happening)
but rather should only allow for one selected element at a time...
The simplest way is to store the previous itemNumber into global variable like previousItemNumber.
Set the initial value of previousItemNumber to -1 (lower than 0) so that it can be used on changeFilter function.
And on changeFilter function, first, you check if previousItemNumber is existed or not (in other words, already selected item is existed or not when selecting new item) and if existed, remove the className there.
Add the className for the selected item and set the current itemNumber to previousItemNumber.
let previousItemNumber = -1;
const changeFilter = (itemNumber) => {
// grab element that has been clicked
let selector = document.getElementsByClassName(itemNumber);
if (previousItemNumber === itemNumber) {
return;
}
// Remove the class from previous selector
if (previousItemNumber >= 0) {
selector[previousItemNumber].classList.remove("darken-filter-selector");
}
selector[itemNumber].classList.add("darken-filter-selector");
previousItemNumber = itemNumber;
};
You can achieve the desired using input type="checkbox" and a bit of JS:
const EL_checkboxesOne = document.querySelectorAll(".checkboxesOne");
const checkboxesOne = (EL_group) => {
const inputs = EL_group.querySelectorAll('[type="checkbox"]');
EL_group.addEventListener("input", (ev) => {
[...inputs].filter(EL => EL !== ev.target).forEach(EL => EL.checked = false);
});
};
EL_checkboxesOne.forEach(checkboxesOne);
.checkboxesOne {
display: flex;
font: bold 16px/1.4 sans-serif;
color: #666;
}
.checkboxesOne input {
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
clip: rect(0 0 0 0);
clip-path: inset(100%);
}
.checkboxesOne span {
padding: 10px 0;
margin: 0 10px;
user-select: none;
cursor: pointer;
}
.checkboxesOne input:checked + span {
box-shadow: 0 4px green;
}
<div class="checkboxesOne">
<label><input type="checkbox" name="filter" value="all"><span>ALL</span></label>
<label><input type="checkbox" name="filter" value="new"><span>NEW</span></label>
<label><input type="checkbox" name="filter" value="wip"><span>WIP</span></label>
<label><input type="checkbox" name="filter" value="hot"><span>HOT</span></label>
<label><input type="checkbox" name="filter" value="won"><span>WON</span></label>
<label><input type="checkbox" name="filter" value="lost"><span>LOST</span></label>
<label><input type="checkbox" name="filter" value="dnc"><span>DNC</span></label>
</div>
I went with this function, as I thought it was the easiest and required the least modification from my original function
const changeFilter = (itemNumber) => {
// grab element that has been clicked
let selector = document.getElementsByClassName(itemNumber);
// check if element doesnt has the class name that underlines it
if (!selector[0].classList.contains("darken-filter-selector")) {
// add the class if it doesnt have it
selector[0].classList.add("darken-filter-selector");
}
// iterate through all the elements, check if it has the class
// remove it if it doesnt have the class
for (let i = 0; i < 7; i++) {
if (i !== itemNumber) {
let otherEls = document.getElementsByClassName(i);
if (otherEls[0].classList.contains("darken-filter-selector")) {
otherEls[0].classList.remove("darken-filter-selector");
}
}
}
};
This is my suggestion. It uses Event Delegation. Change the style as you like.
options.onclick = function(e) {
if (e.target.tagName != "LI") return;
var selected = options.querySelector(".active");
if (selected) {
selected.removeAttribute("class");
}
e.target.setAttribute("class", "active");
console.log(e.target.dataset.option);
};
#options {
display: flex;
justify-content: center;
}
#options li {
margin: 0 10px 0 10px;
padding: 10px;
cursor: pointer;
list-style: none;
}
#options li.active {
background-color: green;
pointer-events: none;
border-radius: 5px;
}
<ul id="options">
<li data-option="all">ALL</li>
<li data-option="new">NEW</li>
<li data-option="wip">WIP</li>
<li data-option="hot">HOT</li>
<li data-option="won">WON</li>
<li data-option="lost">LOST</li>
<li data-option="dnc">DNC</li>
</ul>
You can also do without using Data Attribute (data-option="..."): just use e.target.innerHTML.

Select concrete DOM element without ID

When I'm typing a login or password, tooltip appears with one or more sentences.
Every tooltip has the same z-index, but I want to change it to higher when I'm focused at adequate input and bring it back at blur event, but I might have 10 inputs with many options in tooltip. Is it possible to write function without using ID of tooltip?
$(document).ready(function() {
$('input').focus(function() {
$('div.tooltip').addClass("active");
});
$('input').blur(function() {
$('div.tooltip').removeClass("active");
});
$('#login').keyup(function() {
var th = document.getElementById('login').value;
if (th.length < 6) {
$('#result').css('display', 'inline');
var ex = $("#too_short").text();
if (!ex) {
$('#result').append('<p id="too_short">Too short password.</p>');
}
} else {
$("#too_short").remove();
}
if (th.contains('\'') || th.contains('\"') || th.contains('\\') || th.contains('\/')) {
$('#result').css('display', 'inline');
var en = $("#forb_char").text();
if (!en) {
$('#result').append('<p id="forb_char">Forbidden characters</p>');
}
} else {
$("#forb_char").remove();
}
});
$('#pwd').keyup(function() {
var th = document.getElementById('pwd').value;
if (th.length < 6) {
$('#result1').css('display', 'inline');
var ex = $("#too_short1").text();
if (!ex) {
$('#result1').append('<p id="too_short1">Too short password.</p>');
}
} else {
$("#too_short1").remove();
}
if (th.contains('\'') || th.contains('\"') || th.contains('\\') || th.contains('\/')) {
$('#result1').css('display', 'inline');
var en = $("#forb_char1").text();
if (!en) {
$('#result1').append('<p id="forb_char1">Forbidden characters</p>');
}
} else {
$("#forb_char1").remove();
}
});
});
.tooltip {
position: absolute;
border: 2px solid red;
display: none;
margin-left: 250px;
background: blue;
z-index: 1;
color: white;
}
.active
}
z-index:999;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<form name="form" id="form" method="post">
<div id="result" class="tooltip"></div>
<span> Write login: </span>
<input id="login" name="login" type="text" />
<br/>
<div id="result1" class="tooltip"></div>
<span> Write pwd: </span>
<input id="pwd" name="pwd" type="text" />
</form>
<!-- How to addClass active to proper div.tooltip? -->
Something like this? :
http://jsfiddle.net/9n2db67u/22/
Your code is very messy. Try to use TidyUp when posting jsfiddle links..
$('input').on('click', function () {
$('.active').removeClass('active');
$(this).prevAll('div').addClass('active'); //.css('z-index', '20000');
});

Categories

Resources