I was curious about how I could make a normal button, with a "selected" style or animation.
<button> I'm a button </button>
When you use Radiobuttons, you can see clearly that you have a selected style whenever you click on the button.
<input type="radio">
Now is the question, is it possible to have a selected style or animation (Not a click/hover animation) for the last button that you clicked (Without obviously having to use radiobuttons/checkbox).
If so, how does one make this?
JSFiddle if you want to use the code that I used in the GIF
I haven't found a lot of articles about this, or maybe just haven't looked good enough, anyways, maybe you people know how to do this?
Include the JS in your page.
In your HTML, similarly to what you would do by adding the name and value attributes to your <input type="radio">s you add the data-name and data-value attributes to the elements you wish to behave as <input type="radio">s. If you want one to be selected by default add the attribute/value data-selected="true" to it.
Target the selected elements in CSS with the [data-radio-selected='true'] selector.
document.addEventListener('DOMContentLoaded', () => {
const radioGroups = {}
document.querySelectorAll('[data-radio-name]').forEach( el => {
const name = el.dataset.radioName;
const value = el.dataset.radioValue;
const selected = el.dataset.radioSelected === 'true';
// Register radio
radioGroups[name] = radioGroups[name] || {
radios: [],
selected: null
};
radioGroups[name].radios.push(el);
if ( selected && radioGroups[name].selected == null ) {
radioGroups[name].selected = value;
}
// attach listeners
el.addEventListener('click', () => {
radioGroups[name].radios.forEach( el => {
el.dataset.radioSelected = 'false';
})
el.dataset.radioSelected = 'true';
radioGroups[name].selected = value;
})
})
})
[data-radio-selected='true'] {
background: red;
}
<button data-radio-name="animals" data-radio-value="pig">
Pig
</button>
<button data-radio-name="animals" data-radio-value="cow">
Cow
</button>
<button data-radio-name="animals" data-radio-value="chicken">
Chicken
</button>
Just toggle a class with an onclick event:
btnState = false;
btn.addEventListener("click", () => {
btn.classList.toggle("selected");
btnState = !btnState;
});
In the CSS add:
.selected {
background-color: red;
}
And add an id="btn" to the button in the HTML.
Related
I'm trying to set up a listener for a button that does not have an ID. How would I create a listener for the following element:
<button class="jss459 jss433 jss444 jss445 jss447 jss448 jss321" tabindex="0" type="button" title="Press Enter"><span class="jss434">Quick Search</span><span class="jss471"></span></button>
const myelement = document.querySelector('.jss459.jss433.jss444.jss445.jss447.jss448.jss321');
or
const myelement = document.querySelector('[title="Press Enter"]');
But you'd probably be better off with a more unique selector :)
querySelector()
It allows You to grab the element by the class name
const button = document.querySelector(<classHere>);
button.addEventListener('click', ()=> {
//code here
})
You can use document.querySelector to return the first element that matches the selector, for example:
document.querySelector('button').addEventListener('click', event => {
console.log('The button was clicked');
});
<button class="jss459 jss433 jss444 jss445 jss447 jss448 jss321" tabindex="0" type="button" title="Press Enter"><span class="jss434"> Quick Search</span><span class="jss471"></span> </button>
It sounds to me that you ONLY want to select a button that doesn't have an ID attribute. In that case, you can use querySelectorAll to select all the buttons on the page and then add an Event Listener the buttons that don't have the ID attribute. Below would be the code for that:
let allButtons = document.querySelectorAll('button')
allButtons.forEach( button => {
if (!button.hasAttribute('id')) {
button.addEventListener('click', buttonWithoutId)
}
})
function buttonWithoutId() {
alert('this button does not have an ID!')
}
I have a container with 2 button elements inside of them. Both buttons have the same class name but different attribute values (size).
I have wrapped the buttons in a forEach property and added a click event listener.
Once the event is executed, I am using setAttribute('active', '') on the selected button, but when the other is clicked, it should remove the attribute from the old event, and add it to the current one.
Current situation:
It sets an active attribute on both buttons, but doesn't remove the inactive ones.
Expected result:
The active attribute should be only enabled when I click on the selected button.
Here's a basic snippet:
const getButtons = document.querySelectorAll('.test');
getButtons.forEach(button => {
button.addEventListener('click', () => {
button.setAttribute('active', '')
console.log(button.getAttribute('size'))
});
})
<div class="flex">
<button class="test" size="10">Press Me 10</button>
<button class="test" size="20">Press Me 20</button>
</div>
You need to go deeper. :)
You can use forEach inside an forEach
getButtons.forEach(button => {
button.addEventListener('click', () => {
getButtons.forEach(b => {
//remove all active attributes
b.removeAttribute('active')
})
// add single active attribute
button.setAttribute('active', '')
console.log(button.getAttribute('size'))
});
})
But actually the best way is always native - using radio buttons may be the best solution, because in radio button group there can be only one active button
use jquery toggle()
const getButtons = document.querySelectorAll('.test');
getButtons.forEach(button => {
button.addEventListener('click', () => {
button.toggle("active", function() {
console.log(button.getAttribute('size'))
});
});
})
You can remove the active attribute from buttons before applying active to new button. :)
getButtons.forEach(button => {
button.addEventListener('click', (e) => {
document.querySelectorAll("button[active]").forEach(button => button.removeAttribute('active'))
button.setAttribute('active', '')
console.log(button.getAttribute('size'))
});
})
Given a script that toggles between two classes, I'm also adding an active state to the currently selected link to set an underline to show active state. However, when clicking the link continuously, it keeps adding the active class, rather than toggling the class on and off. How can I get the active state to show when the link is clicked, and switch off and apply to the other link when the other link is clicked?
JS
const Terms = {
bindEvents () {
this.enTrigger.addEventListener('click', (e) => {
this.langToggle(this.englishContent)
this.enTrigger.classList.add('active')
this.frTrigger.classList.remove('active')
})
this.frTrigger.addEventListener('click', (e) => {
this.langToggle(this.frenchContent)
this.frTrigger.classList.add('active')
this.enTrigger.classList.remove('active')
})
},
init () {
this.englishContent = document.getElementById('english-terms')
this.frenchContent = document.getElementById('french-terms')
this.enTrigger = document.getElementById('en')
this.frTrigger = document.getElementById('fr')
this.bindEvents()
},
langToggle (id) {
this.englishContent.style.display = 'none'
this.frenchContent.style.display = 'none'
id.style.display = 'block'
}
}
document.addEventListener('DOMContentLoaded', () => {
Terms.init()
})
HTML
<div class="terms-nav">
<a id="en">English</a><a id="fr">French</a>
</div>
You could try using something like this to toggle between the two classes. So when you click on one element, it adds the class to itself but removes it from the other one.
JS Fiddle
var english = document.getElementById('en');
var french = document.getElementById('fr');
english.addEventListener('click', function(){
this.classList.add('active');
french.classList.remove('active');
});
french.addEventListener('click', function(){
this.classList.add('active');
english.classList.remove('active');
});
I am having some difficulty using parentNode.removeChild(). I have a list of 'items' in an un-ordered list, each have there own delete button. I am trying bind a click event to each individual button that will delete it's respective parent 'item'.
My code so far:
<ul class="list">
<h2>This is a list</h2>
<li class="item">
<h3>Some Item</h3>
<button class="delete">Delete</div>
</li>
<li class="item">
<h3>Some Item</h3>
<button class="delete">Delete</div>
</li>
<li class="item">
<h3>Some Item</h3>
<button class="delete">Delete</div>
</li>
</ul>
var childElements = document.getElementsByClassName('item');
var buttonElement = document.getElementsByClassName('delete');
function deleteItem(buttonsClass, childClass) {
for (var i=0;i<buttonsClass.length;i++) {
var child = childClass[i];
buttonsClass[i].addEventListener('click', function(child) {
childClass[i].parentNode.removeChild(childClass[i]);
}, false);
}
}
deleteItem(buttonElement, childElements);
I know there is an easier way to do this with jQuery but i really want to solve this with plain javascript. Thank you for any and all help.
This is a perfect case for event delegation. No need for jQuery at all:
(function(window, htmlElement) {
'use strict';
htmlElement.addEventListener("click", handleClick, false);
function handleClick(event) {
if (event.target.classList.contains("delete")) {
event.preventDefault();
removeItem(event.target);
}
}
function removeItem(button) {
var item = getItem(button),
confirmMessage;
if (item) {
confirmMessage = item.getAttribute("data-confirm");
if (!confirmMessage || window.confirm(confirmMessage)) {
item.parentNode.removeChild(item);
}
}
else {
throw new Error("No item found");
}
}
function getItem(button) {
var element = button.parentNode,
item = null;
while (element) {
if (element.nodeName === "LI" || element.nodeName === "TR") {
item = element;
break;
}
element = element.parentNode;
}
return item;
}
})(this, this.document.documentElement);
You have one click handler for the entire page, regardless of how many delete buttons you have. This should also work for list items or table rows, and by specifying a data-confirm attribute on your buttons, it will pop up a confirm box before removing it.
<button type="button" class="delete"
data-confirm="Are you sure you want to delete this item?">
Delete
</button>
You can also easily change this so it uses another attribute to find the delete button:
<button type="button" class="delete"
data-delete
data-confirm="...">
Delete
</button>
Just change the condition of the if statement in the handleClick function to:
if (event.target.hasAttribute("data-delete")) {
event.preventDefault();
removeItem(event.target);
}
This decouples your behavior from styling. You can use the delete class name for styling, and the data-delete attribute for JavaScript behavior. If you need to change the name of the CSS delete class for any reason, or use a different class, because you decide to use a third party CSS framework like Bootstrap, then you don't need to change a single line of JavaScript.
The last advantage here is that the click handler is attached to the document.documentElement object, which is available the moment JavaScript begins executing and represents the <html> element. No need for jQuery's document ready event handler. Just attach the click handler and import the script at any point on the page.
The problem is that your childClass[i] that you call when you click an element, is not what you expect when you define the function.
You should use event.target for catch the element clicked
var childElements = document.getElementsByClassName('item');
var buttonElement = document.getElementsByClassName('delete');
var _handler = function(e) {
e.target.parentNode.parentNode.removeChild(e.target.parentNode);
}
function deleteItem(buttonsClass, childClass) {
for (var i=0;i<buttonsClass.length;i++) {
buttonsClass[i].addEventListener('click', _handler, false);
}
}
deleteItem(buttonElement, childElements);
-- edit --
If you want to use the original approach, then you can solve it in this way:
function deleteItem(buttonsClass, childClass) {
for (var i=0;i<buttonsClass.length;i++) {
(function(child) {
buttonsClass[i].addEventListener('click', function(e) {
child.parentNode.removeChild(child);
}, false);
})(childClass[i]);
}
}
With encapsulation (function(encapsulatedChild) { })(child) you can store the value of child in a context that does not change during the next cycle.
Looks like you want to bind a click event to delete buttons, and on that event delete that item.
You need to fetch the child classes individual buttonsClass elements.
function deleteItem( childElements )
{
Array.prototype.slice.call( childElements ).forEach( function( item ){
var deleteChildren = item.getElementsByClassName( "delete" );
Array.prototype.slice.call( deleteChildren ).forEach( function( deleteBtn ){
deleteBtn.addEventListener('click', function()
{
this.parentNode.removeChild( this );
}, false);
});
});
}
or even more simply, just pass the list of buttons on clicking which parent item will be deleted
function deleteItem( buttonElement )
{
Array.prototype.slice.call( buttonElement ).forEach( function( button ){
button.addEventListener('click', function()
{
this.parentNode.removeChild( this );
}, false);
});
}
I have a <div> that exists on a page and I need to make it so that when the user clicks outside of that element it will become hidden, but if the user clicks somewhere within the element, then it should stay.
I tried using
e.stopPropagation();
and
e.preventDefault();
adding it to the click event of that certain DIV but that didn't work.
Thanks!
Assign the desired event listener (like "click") to document or window using EventTarget.addEventListener()
Use Event.target in combination with Element.closest() as negation ! - in order to check whether the Event.target (the element that initiated the Event) - its self or closest ancestor have a specific selector.
To control an element visibility create a CSS class that does the necessary styling, and use Element.classlist to add, remove or toggle that class (as needed).
const elPopup = document.querySelector("#popup");
addEventListener("click", (evt) => {
if (!evt.target.closest("#popup")) elPopup.classList.remove("isOpen");
});
#popup {
padding: 2rem;
background: gold;
display: none; /* Hidden popup by default */
}
#popup.isOpen {
display: block;
}
<div id="popup" class="isOpen">
Click outside to close me.<br>
Click inside will do nothing.
</div>
Never use Event.stopPropagation() unless you really, really know what you're doing. Your app or third-party code should be always notified about all events happening in their context.
Usage example: Close popup modal on click outside
Probably the easiest way to do this will be to monitor clicks on the entire document, and ignore it if it's not that element. If it is, then hide it.
(function(div) {
$(document).click(function(e) {
if (e.srcElement !== div) $(div).hide();
});
})($('div')[0]);
Edit: Derp, misunderstood, click inside should stay, otherwise hide... invert the equality check.
http://jsfiddle.net/robert/QcPx4/
useOuterClick
Hi . you can create custom hook like this:
export const useOuterClick = (elementRef, setElementVisibility) => {
useEffect(() => {
document.addEventListener('click', handleClick);
return () => document.removeEventListener('click', handleClick);
function handleClick(e: any) {
if (elementRef && elementRef.current) {
const ref: any = elementRef.current;
if (!ref.contains(e.target)) {
setElementVisibility(false);
}
}
}
}, [])};
then use it this way in your component:
import { useState, useRef } from 'react';
import useOuterClick from './hooks/useOuterClick';
export const SampleComponent = () => {
const [activeElement, setActiveElement] = useState(false);
const elementRef = useRef();
useOuterClick(elementRef, setActiveElement);
return (
<>
<div ref={elementRef}>
<button
onClick={() => setActiveElement(!activeElement)}>
'this button can open and close div'
</button>
{activeElement &&
<div>'this div will be hidden if you click on out side except
button'
</div>
}
</div>
</>
);
};