Why several clicks fire for each instance of a Class? - javascript

I have three tooltip buttons on a page. I can close any open tooltip by clicking anywhere outside buttons. And this is what I came across:
In the code below, when I click on any place on the page, the handler of this part of code is activated $(document).on('click', (event) => this.closeOnOutsideClick(event));
I can see in inspector, that function closeOnOutsideClick is fired three times - it makes three checks for each tooltip button present on the page. I cannot figure out what mechanism is responsible for that and why the check if (!$(event.target).closest(this.$elem)) is not performed only once? My code can be found here and also below: https://jsfiddle.net/bakrall/786cz40L/
This is a simplified version of a more complex code just to give example of my issue:
const selectors = {
tooltip: '.tooltip-container',
tooltipButton: '.tooltip-button',
tooltipMessage: '.tooltip-message'
}
class Tooltip {
constructor(tooltip) {
this.$elem = $(tooltip);
this.$tooltipButton = this.$elem.find(selectors.tooltipButton);
this.$tooltipMessage = this.$elem.find(selectors.tooltipMessage);
this.$tooltipMessageText = this.$tooltipButton.attr('data-tooltip-content');
this.bindUiEvents();
}
bindUiEvents() {
$(document).on('click', (event) => this.closeOnOutsideClick(event));
this.$tooltipButton.on('click', () => this.showTooltipMessage());
this.$tooltipButton.on('blur', () => this.hideTooltip());
}
showTooltipMessage() {
this.$tooltipMessage
.text(this.$tooltipMessageText)
.addClass('shown-message');
}
hideTooltip() {
this.$tooltipMessage
.text('')
.removeClass('shown-message');
}
closeOnOutsideClick(event) {
if (!$(event.target).closest(this.$elem)) {
this.hideTooltip();
}
}
}
//class in another file
const tooltip = $('.tooltip-container');
tooltip.each(function(index, item) {
new Tooltip(item);
})
.input-wrapper {
margin-bottom: 2em;
}
.tooltip-container {
position: relative;
display: inline-block;
}
.tooltip-message {
display: none;
position: absolute;
left: 100%;
top: 0;
width: 10em;
padding: 0.5rem;
background: #000;
color: #fff;
}
.tooltip-message.shown-message {
display: inline-block;
}
button {
width: 1.2em;
height: 1.2em;
border-radius: 50%;
border: 0;
background: #000;
font-family: serif;
font-weight: bold;
color: #fff;
}
button:focus {
outline: none;
box-shadow: 0 0 0 0.25rem skyBlue;
}
input {
display: block;
}
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title> </title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="tooltip.css">
</head>
<body>
<div class="input-wrapper">
<label for="name">
What's your name?
<span class="tooltip-container">
<button class="tooltip-button" type="button" aria-label="more info"
data-tooltip-content="This clarifies whatever needs clarifying">i</button>
<span class="tooltip-message" role="status"></span>
</span>
</label>
<input id="name" type="text"/>
</div>
<div class="input-wrapper">
<label for="age">
What's your age?
<span class="tooltip-container">
<button class="tooltip-button" type="button" aria-label="more info"
data-tooltip-content="This is to know how old you are">i</button>
<span class="tooltip-message" role="status"></span>
</span>
</label>
<input id="age" type="text"/>
</div>
<div class="input-wrapper">
<label for="nationality">
What's your nationality
<span class="tooltip-container">
<button class="tooltip-button" type="button" aria-label="more info"
data-tooltip-content="What country are you from?">i</button>
<span class="tooltip-message" role="status"></span>
</span>
</label>
<input id="nationality" type="text"/>
</div>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous">
</script>
<script src="tooltip.js" async defer></script>
</body>
</html>

tooltip.each(function(index, item) {
new Tooltip(item);
})
Since you instantiate 3 Tooltips, you bind a separate event listener to the document each time. Each listener is getting triggered with each click. However, each of those listeners has a different this which is what allows each listener to tell if its Tooltip was clicked and if not, hide it.
If you want a single listener you could store a list of all your Tooltips and have the event listener iterate through the list of Tooltips, closing all Tooltips that were not clicked.

Your click event is firing on mutliple elements, because you specified just (document). Maybe you can be more specific:
$(document).on('click', '.input-wrapper', (event) => this.closeOnOutsideClick(event));

Related

How to change css property when you click on button?

I want when i click in each one of them to pop the span element with class color but only for that button.
[![enter image description here][1]][1]
I only achieved when i click on one of them to change the visibility to visible on all elements with class colors.
my Code : https://jsbin.com/govowakasa/edit?js
document.getElementById("btn-apple").addEventListener("click", function(e) {
showColor()
});
function showColor() {
const colors = document.getElementsByClassName("color");
for (let i=0; i<colors.length; i++) {
colors[i].style.visibility = 'visible';
}};
You can simply add this line
fruits[i].style.backgroundColor = 'red'
You can not use the same ID for more than 1 element.
I replaced the IDs of buttons with a new class name fruit-btn.
Then I made an array in JS of both buttons and color spans.
Then I looped through each button to add an EventListener.
In the event I added another loop that hides every color span and then makes the i span visible. There is no need for a function.
const fruit_btn = document.getElementsByClassName("fruit-btn");
const color_spans = document.getElementsByClassName("color");
for (let i = 0; i < fruit_btn.length; i++) {
fruit_btn[i].addEventListener("click", function(e) {
for (let i = 0; i < fruit_btn.length; i++) {
color_spans[i].style.visibility = "hidden";
}
color_spans[i].style.visibility = "visible";
});
}
body {
font-size: 2rem;
}
li {
margin: 20px;
list-style: none;
}
button i {
cursor: pointer;
font-size: 3rem;
}
.color {
margin: 10px;
border: 1px solid black;
background-color: blanchedalmond;
display: inline-block;
padding: 10px;
visibility: hidden;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fruits</title>
<link rel="stylesheet" href="list.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" integrity="sha512-0S+nbAYis87iX26mmj/+fWt1MmaKCv80H+Mbo+Ne7ES4I6rxswpfnC6PxmLiw33Ywj2ghbtTw0FkLbMWqh4F7Q==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
</head>
<body>
<p>
FRUITS
</p>
<ul>
<li class="items">
<button class="fruit-btn"><i class="fas fa-apple-alt"></i></button>
<span class="color">red and yellow</span>
</li>
<li class="items">
<button class="fruit-btn"><i class="fas fa-carrot"></i></button>
<span class="color">orange</span>
</li>
<li class="items">
<button class="fruit-btn"><i class="fas fa-lemon"></i></button>
<span class="color">yellow</span>
</li>
<li class="items">
<button class="fruit-btn"><i class="fas fa-pepper-hot"></i></button>
<span class="color">red and green</span>
</li>
</ul>
<script src="/week-4/index.js"></script>
</body>
</html>
I would suggest to add/remove a Class instead of adding/removing directly CSS styling.

Toggle paragraph using link from another paragraph

I have similar code like this. Except for the fact that my JS file is external. The function myFunction() is called in the beginning of that file, like this:
document.addEventListener('DOMContentLoaded', function () {
myFunction()
})
But, it's not working - it's not showing the text from the second paragraph, when clicked on the word HERE from the first paragraph. I assume that maybe I should call the function somehow else. Any given ideas would be really helpful. Thanks.
P.S. Also, when clicked, the window should scroll to the newly opened paragraph.
function myFunction() {
var x = document.getElementById("myCollapsible2")
if (x.style.display === "block") {
x.style.display = "none";
} else {
x.style.display = "block";
}
}
p{
background: #eee;
border: 1px solid #ccc;
margin: 20px 0;
padding: 30px;
}
.bs-example{
margin: 20px;
}
.link-color {
color: red;
}
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js">.</script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="bs-example">
<input type="button" class="btn btn-primary" data-toggle="collapse" data-target="#myCollapsible" value="Toggle Button">
<div id="myCollapsible" class="collapse"><p>This is a simple example of expanding and collapsing individual element via data attribute. <br>Click on the <b>Toggle Button</b> button to see the effect. If you click <a onclick="myFunction()" class="link-color" href="#">here</a>, you should see the other paragraph. </p></div>
<div id="myCollapsible2" class="collapse"><p>This is a simple example</p></div>
</div>
</body>
</html>
Since you are using bootstrap with popper, your function is unnecessary, just set the values according to the documentation. To scroll to the element, use the scrollIntoView method. I had to wrap it around a setTimeout, because the default method was executing afterwards, so when I called scroll the element wasn't visible yet.
function scrollTo2() {
var el = document.getElementById('myCollapsible2');
window.setTimeout(() => el.scrollIntoView(), 0);
}
p {
background: #eee;
border: 1px solid #ccc;
margin: 20px 0;
padding: 30px;
}
.bs-example {
margin: 20px;
}
.link-color {
color: red;
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<div class="bs-example">
<input type="button" class="btn btn-primary" data-toggle="collapse"
data-target="#myCollapsible" value="Toggle Button">
<div id="myCollapsible" class="collapse show">
<p>This is a simple example of expanding and collapsing individual element
via data attribute.<br>Click on the <b>Toggle Button</b>
button to see the effect.
If you click <a onclick="scrollTo2()" data-toggle="collapse"
data-target="#myCollapsible2" class="link-color" href="#">here</a>,
you should see the other paragraph. </p>
<div id="myCollapsible2" class="collapse">
<p>This is a simple example</p>
</div>
</div>
</div>

Add enterkeypress function for adding list in javascript and organize the list style properly in todo

I want to add enterfunction into Javascript for my TODO list project,in which I can press the enter key and the item should be added, now I am able to add list through add button only.
Secondly, I want to organize The List properly for TODO list by giving some style. rightnow it doesn't looks good.
Rightnow my TODOLIST project looks like this https://i.stack.imgur.com/V1j5z.png
var add = document.getElementById('add');
var removeall = document.getElementById('removeall');
var list = document.getElementById('list');
//remove all button
removeall.onclick= function(){
list.innerHTML= '';
}
//adding a list element
add.onclick = function(){
addlis(list);
document.getElementById('userinput').value= '';
document.getElementById('userinput').focus();
}
function addlis(targetUl){
var inputText = document.getElementById('userinput').value;
var li = document.createElement('li');
var textNode= document.createTextNode(inputText + ' ');
var removeButton= document.createElement('button');
if(inputText !== ''){
removeButton.className= 'btn btn-xs btn-danger';
removeButton.innerHTML= '×';
removeButton.setAttribute('onclick','removeMe(this)');
li.appendChild(textNode); //onebelowanother
li.appendChild(removeButton);
targetUl.appendChild(li);
} else{
alert("Please enter a TODO");
}
}
function removeMe(item){
var p = item.parentElement;
p.parentElement.removeChild(p);
}
body{
font-family: 'EB Garamond', serif;
padding: 0;
margin: 0;
background-color: beige;
}
h1{
margin: 40px;
}
#userinput{
width: 350px;
height: 35px;
display: inline-block;
}
.btn{
margin: 20px 5px;
}
.form-control{
margin: 0 auto;
}
ul{
list-style: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" media="screen" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="css/TodoStyle.css">
<link href="https://fonts.googleapis.com/css?family=EB+Garamond&display=swap" rel="stylesheet">
<title>My Online TODO List App</title>
</head>
<body>
<div class="container-fluid text-center">
<h1>My First working TODO</h1>
<input type="text" id="userinput" class="form-control" placeholder="what you need to do" onkeydown="return searchKeyPress(event);">
<button class="btn btn-success" id="add">Add a TODO</button>
<button class="btn btn-danger" id="removeall">REMOVE ALL TODO</button>
<ul id="list">
<li class="list"></li>
</ul>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script src="js/Todo.js"></script>
</body>
</html>
Just wrap all the inputs and buttons inside a form tag like this:
<form id="...">
<input type="text" id="userinput" class="form-control" placeholder="what you need to do" onkeydown="return searchKeyPress(event);">
<button type="submit" class="btn btn-success" id="add">Add a TODO</button>
</form>
and replace this:
add.onclick = function(){...})
with
form = document.getElementBy...
form.addEventListener('submit', function() {...})
Also try to avoid writing add.onclick and use addEventListener. That way you can have multiple listeners, remove them easily, and overall have more control.
Use event listener for key press on your input (13 is the enter key):
var input = document.getElementById("userinput");
input.addEventListener("keyup", function(event) {
if (event.keyCode === 13) {
event.preventDefault();
searchKeyPress(event);
}
});

Bootstrap form validation with Javascript

I have a bootstrap form that I'd like to validate using Javascript by clicking on a link (NOT submit button). Here's my sample code.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title></title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
<style>
/* ==========================================================================
Demo using Bootstrap 3.3.4 and jQuery 1.11.2
You don't need any of the following styles for the form to work properly,
these are just helpers for the demo/test page.
========================================================================== */
#wrapper {
width:595px;
margin:0 auto;
}
legend {
margin-top: 20px;
}
#attribution {
font-size:12px;
color:#999;
padding:20px;
margin:20px 0;
border-top:1px solid #ccc;
}
#O_o {
text-align: center;
background: #33577b;
color: #b4c9dd;
border-bottom: 1px solid #294663;
}
#O_o a:link, #O_o a:visited {
color: #b4c9dd;
border-bottom: #b4c9dd;
display: block;
padding: 8px;
text-decoration: none;
}
#O_o a:hover, #O_o a:active {
color: #fff;
border-bottom: #fff;
text-decoration: none;
}
#media only screen and (max-width: 620px), only screen and (max-device-width: 620px) {
#wrapper {
width: 90%;
}
legend {
font-size: 24px;
font-weight: 500;
}
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript">
</script>
<script type="text/javascript" src="<?php echo base_url('scripts/js/validator.min.js'); ?>"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script> <!-- only added as a smoke test for js conflicts -->
</head>
<body>
<div id="wrapper">
<form data-toggle="validator" role="form" id="form" name="extra" value="me">
<div id="entry1" class="clonedInput">
<h2 id="reference" name="reference" class="heading-reference">Entry #1</h2>
<fieldset>
<!-- Select Basic -->
<label class="label_ttl control-label" for="title">Title:</label>
<!-- Text input-->
<div class="form-group">
<label class="label_fn control-label" for="first_name">First name:</label>
<input id="first_name" name="first_name" type="text" placeholder="" class="form-control" required>
<p class="help-block">This field is required.</p>
</div>
</div><!-- end #entry1 -->
<!-- Button -->
<p>
Submit
</p>
</fieldset>
</form>
</div> <!-- end wrapper -->
<script>
function myFunction(){
$('#form').validator().on('submit', function (e) {
if (e.isDefaultPrevented()) {
// handle the invalid form...
} else {
// everything looks good!
}
})
}
</script>
</body>
</html>
Am using this lib for validation
http://1000hz.github.io/bootstrap-validator/#validator-usage
I need 'onclick' to fire up the JS function and run validation which should give me a true(i.e pass) or false(i.e fail).
Note: Am a complete noob in JS and am essentially trying to cobble up this so that it works for me.
You could call a function on click of the link such as:
function myFunction(){
$("#form").submit(function() {
$(this).validator(function(e) {
if (e.isDefaultPrevented()) {
// handle the invalid form...
} else {
// everything looks good!
}
});
});
}
Source: https://api.jquery.com/submit/
I'd recommend using query validate (http://jqueryvalidation.org). It has lots options and works very well with bootstrap. You can also check fields before submission using valid() method or perform validation using validate() method.
Here is a quick example https://jsfiddle.net/Lu165LLt/1/
<form id="myform" class="container">
<div class="form-group">
<label class="control-label" for="name">Name</label>
<input type="text" name="name" class="form-control" required />
</div>
<a>Validate!</a>
</form>
$.validator.setDefaults({
debug: true,
success: "valid"
});
var form = $("#myform");
form.validate();
$("a").click(function () {
alert("Valid: " + form.valid());
});
Libraries Used:
jQuery,jQuery Validate,Bootstrap

Create popup box on click with value of hidden element

Hi i have looked every where for this but cant find a good way of doing it :/
I have a dynamically populated list on my website that looks like this:
<li>
name: blue<br/>
procesor: blue<br/>
<div class="right top">2001</div>
<input type="hidden" name="Description" value="short">
</li>
I have been looking for a way to have a small popup show with the contents of the hiddent text field in the list.
I have tried many free modal javascript code but they all seem to either stop working or destroy my template.
Dose anyone know how I could do this. I have no working javascript code as from now and am looking for the best way todo this. It doesn't need to be fancy. Just a white boc with the popup
The easiest way is to add title property to li:
<li title="Some short desc">
...
</li>
This should work on all desktop browsers, but not on mobiles.
Another way is to use excellent hint.css from http://kushagragour.in/lab/hint/
It does not rely on any JavaScript and rather uses data-* attribute, pseudo elements, content property and CSS3 transitions to create the tooltips. You can fine tune orientation, colours, and even animations.
Try this one
$(function() {
$(".list li input[type=hidden]").each(function(index, obj) {
$(".popup").append($(obj).val() + "<br/>");
});
$(".popup").show();
});
.popup {
position: relative;
top: 10px;
border: solid 2px #E5988A;
width: 200px;
text-align: left;
margin: 0px auto;
z-index: 2;
background-color: #FFF;
padding: 10px;
}
.disabled {
position: absolute;
left: 0px;
top: 0px;
right: 0px;
bottom: 0px;
z-index: 1;
display: block;
background-color: #333;
opacity: 0.5;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<ul class="list">
<li>
name: blue
<br/>procesor: blue
<br/>
<div class="right top">2001</div>
<input type="hidden" name="Description" value="short" />
</li>
<li>
name: green
<br/>procesor: gree
<br/>
<div class="right top">2002</div>
<input type="hidden" name="Description" value="anothershort">
</li>
</ul>
<div class="popup"></div>
<div class="disabled"></div>
Html Code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script src="app1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<link rel="stylesheet" href="http://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css"/>
</head>
<body>
<div>
Select <select id = "MyList"></select>
</div>
<input type="hidden" name="Description" value="short" id="hiddendiv">
</body>
</html>
Javascript
$(document).ready(function(){
var select = document.getElementById("MyList");
var options = ["1", "2", "3", "4", "5"];
for (var i = 0; i < options.length; i++) {
var opt = options[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
select.appendChild(el);
}
$('#MyList').hover(
function(){
var value=$('input').val();
alert(value);
}, function() {
}
)
});

Categories