icon not clickable with onclick event - html

The onclick event only works with the button. If I click on the icon in the button the function does not work.
enter code here
<div class="dropdown">
<button onclick="myFunction()" class="elementor-button-link elementor-button elementor-size-md elementor-animation-grow" id="dropbtn">
<i onclick="myFunction() "class="far fa-arrow-alt-circle-down">
<script>/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myFunction() {
document.getElementById("myDropdown").classList.toggle("show");
}
// Close the dropdown if the user clicks outside of it
window.onclick = function(event) {
if (!event.target.matches('#dropbtn')) {
var dropdowns = document.getElementsByClassName("dropdown-content");
var i;
for (i = 0; i < dropdowns.length; i++) {
var openDropdown = dropdowns[i];
if (openDropdown.classList.contains('show')) {
openDropdown.classList.remove('show');
}
}
}
}
</script>
enter code here

Short answer: Try using element.closest() instead of element.matches().
I suspect it's the condition in your if statement. By the looks of it, element.matches() will match exactly the ID provided - I.e., it'll match if the element clicked on has that ID, but not if you click on a child element that does not explicitly have the ID, even if the parent does.
According to Mozilla web docs, element.closest() should also match any elements whose parents match the condition provided, even if the element itself doesn't.
Bear in mind this will change the type of the returned value - matches() returns a boolean, while closest() returns the 'closest' element matching the condition.

Related

(Angular) Clicking on a button highlights another button because I'm changing a value in an array. Why?

So I'm trying to make a website using Angular and have come across a very peculiar bug.
Basically I have an *ngFor div that creates 12 buttons.
<div *ngFor = "let color of colors; let i = index" style = "display: inline;">
<button (click) = "toggleTrue(i)" id = "{{i}}" class = "coloredButton">
</button>
</div>
When a button is clicked then it should highlight the button and then change a value in an array within my component like so.
toggleTrue(id: number){
this.colors[id] = !this.colors[id];
var button = document.getElementById(id.toString());
if(this.colors[id] === true){
button.style.border = "10px";
button.style.borderColor = "white";
button.style.borderStyle = "solid";
}
else{
button.style.borderStyle = "none";
}
}
However for some reason whenever I click a button it highlights the button next to it and if I click it twice more than it will highlight a button two buttons away for some reason, click it twice more and it'll highlight a button three buttons away, etc. It's not highlighting the right button, but the id it passes through to the function is correct, I checked using console.log(id).
I've messed around a bit (taking out classes and lines and whatnot) and I've isolated the problem to this line.
this.colors[id] = !this.colors[id];
If I remove this then everything works fine (except of course I can't edit the value of the array anymore). This is proven further when I remove the click function of my buttons and the default highlight for buttons correctly highlights the right button.
Can anyone please help me out and tell me why it's not highlighting correctly? Any help would be appreciated.
If you're curious I created the array earlier within the component using colors = [] and it's an array of booleans that I filled within the constructor using a basic for loop.
https://codesandbox.io/s/crimson-brook-wn3q0?file=/src/app/app.component.html
Here is a sandbox with a solution.
Essentially you want to keep styling in the template through the use of NgClass or NgStyle
and use the component just for changing state, avoid accessing the DOM directly due to lifecycle problems.
Typescript
export class AppComponent {
colors: boolean[];
constructor() {
this.colors = [];
for (let i = 0; i < 10; i++) {
this.colors.push(false);
}
}
toggleTrue(id: number) {
this.colors[id] = !this.colors[id];
}
}
HTML
<div>
<div *ngFor="let color of colors; index as i">
<button
(click)="toggleTrue($event, i)"
id="{{i}}"
[ngClass]="{'active':colors[i]==true}"
>
button
</button>
</div>
</div>
CSS
.active {
color: red;
}
.inactive {
color: blue;
}
You could do some changes in your code. Firs in your HTML:
<button *ngFor="let color of colors; let i = index" (click) = "toggleTrue(i)" class = "coloredButton" [class.active]="color"> {{i}} < /button>
You noticed about [class.active]="color" this sentence verify if color is true the button will have the active class
.active {
border: 10px solid white;
}
I don't know how you generate colors array but I assumed somethings and also change your function in the class component
colors = new Array(10).fill(false);
toggleTrue(index: number) {
this.colors[index] = !this.colors[index]
}

JQuery populate div with link content but also need to move (like anchor link) to area where div located

I have unordered list of links. Using JQuery, when clicked, the link's contents (a div with image and text) are loaded into the section specified. This all works beautifully. But I'm wondering how to also get the onclick function to move the view to the div's location on the page similarly to how anchor tag works. Here is the site where you can see the div being populated, but not moving down to view it. https://www.thecompassconcerts.com/artists.php
My JQuery knowledge is not awesome (I'm being generous).
I followed Osama's suggestion to add event listener and I got almost correct results. Upon first click...contents are loaded but do not move. But on every successive click, it functions perfectly: Contents loaded and move to div (like an anchor link) works! BUT...not on Safari or Mobile Safari.
Here is my jQuery. I assume if first click is not working that I must add listener before the first click?? Can the event listeners be added on page load BEFORE the function to prevent default click, etc.?
<script>
// BEGIN FUNCTION TO CAPTURE AND INSERT CONTENT
$(document).ready(function () {
// PREVENT DEFAULT LINK ACTION
$('.bio').click(function (e) {
e.preventDefault();
// ADD LISTENER TO EACH ITEM BY CLASS
var list = document.getElementsByClassName("bio");
for (let i = 0; i < list.length; i++) {
list[i].onclick = moveToDiv;
}
// FUNCTION TO MOVE TO LOCATION
function moveToDiv() {
document.location = "#performbio";
}
// STORE the page contents
var link = $(this).attr("href");
// load the contents into #performbio div
$('#performbio').load(link);
});
});
</script>
Here is the HTML with links in unordered list
<!-- CONTRIBUTING ARTISTS LIST AND BIOS -->
<section id="artists">
<h2>Contributing Artists</h2>
<ul class="cols">
<li><a class="bio" href="performers/first-last.html">First Last</a></li>
<li><a class="bio" href="performers/first-last.html">First Last</a></li>
<li><a class="bio" href="performers/first-last.html">First Last</a></li>
</ul>
</section>
Here is HTML of Section where code is being inserted by function
<!-- Performer Bios Dynamically updated -->
<section id="performbio">
</section>
Here is div contents that are being inserted
<div class="artistbio">
<p class="artistname">First Last</p>
<img class="artistimg" src="performers/img/name.jpg">
<p>lots of text here</p>
</div>
If I understand it right, you want to scroll to the section where the details appear on clicking any item in the list but through js and not HTML. In that case, you would add an onclick listener on to the list elements like so:
listElement.onclick = moveToDiv;
The function:
function moveToDiv() {
document.location = "#performbio";
}
A simple way to add a listener to all of the elements:
var list = document.getElementsByClassName("bio");
for (let i = 0; i < list.length; i++) {
list[i].onclick = moveToDiv;
}
For the edited post, you need to move the function definition out of the document.ready function. you would change the script to:
// FUNCTION TO MOVE TO LOCATION
function moveToDiv() {
document.location = "#performbio";
}
$(document).ready(function () {
// PREVENT DEFAULT LINK ACTION
$('.bio').click(function (e) {
e.preventDefault();
// ADD LISTENER TO EACH ITEM BY CLASS
var list = document.getElementsByClassName("bio");
for (let i = 0; i < list.length; i++) {
list[i].onclick = moveToDiv;
}
// STORE the page contents
var link = $(this).attr("href");
// load the contents into #performbio div
$('#performbio').load(link);
});
});
Another Solution: Using scrollIntoView
First, get all the elements into a variable using querySelectorAll
var elements = document.querySelectorAll(".bio");
Then create a function, for the scrolling part:
function scroll(element) {
element.scrollIntoView();
}
Then just add the onclick listener:
for (let i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', function() {
scroll(elements[i]);
});
}
I found it very frustrating to try to accomplish these two tasks so instead of a jQuery solution I opted for a CSS solution.
I populated my DIV with all the php includes, gave them unique id's for the anchors to work and then used CSS to hide them by default until clicked and it works like a charm....shows only what I need to show and goes there like an anchor is supposed to.
I must thank Ghost for all of your help and efforts to try and solve this via jQuery. You were very kind and generous.
Here is the code I used:
My collection of links.
<li><a class="bio" href="#artist-name1">Name 1</a></li>
<li><a class="bio" href="#artist-name2">Name 2</a></li>
which anchors to these divs
<div class="bio-container" id="artist-name1">
<?php include('performers/name-lastname.html'); ?>
</div>
<div class="bio-container" id="artist-name2">
<?php include('performers/name-lastname.html'); ?>
</div>
Then I use this CSS to hide those divs until the anchors are clicked.
I'm using [id*="artist-"] to target only links with such text...very easy. Not ideal for a massive list...but mine is not so large so it will do for this situation.
[id*="artist-"] {display: none;}
[id*="artist-"]:target {display: block;}

Toggle Text between multiple buttons

I would like to have two buttons which are basically categories. Let's name them category A and category B. The are displayed left and right. Below i would like to display some text which is dependent of the chosen category (i.e the clicked button) so that category A shows text A and category B shows text B.
This if for html. I'm working on a wordpress homepage.
I was able to install one button which toggles text (basically button 1 = Category A). But i couldn't manage to insert a second button (basically button 2 = Category B). Any ideas? Highly appreciated!
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<p>Click the button to swap the text of the DIV element:</p>
<p><button onclick="myFunction()">Click Me</button></p>
<div id="myDIV">Hello</div>
<script>
function myFunction() {
var x = document.getElementById("myDIV");
if (x.innerHTML === "Hello") {
x.innerHTML = "Swapped text!";
} else {
x.innerHTML = "Hello";
}
}
</script>
</body>
</html>
I expect to have 2 buttons which display 2 categories, the text should toggle according to which button has been clicked.
Could put the description in an attribute, then get the attributes value on click and change the html of the description. Here is a jsFiddle
<div>
<button class="js-button default-button" data-description="Category A's Description" onclick="myFunction(this)">
Category A
</button>
<button class="js-button default-button" data-description="Category B's Description" onclick="myFunction(this)">
Category B
</button>
</div>
<div id="js-description" class="description">
</div>
<script>
function myFunction(elem) {
var x = document.getElementById("js-description");
var description = elem.getAttribute('data-description');
x.innerHTML = description;
var button = document.getElementsByClassName('js-button');
for (var i = 0; i < button.length; i++) {
button[i].classList.remove('active-button');
}
elem.classList.add('active-button');
}
</script>
<style>
.default-button{
font-size:16px;
border-radius: 4px;
padding:7px 12px;
}
.active-button{
background:blue;
color:#fff;
}
.description{
margin-top:20px;
}
</style>
I don't really like all these solutions because everything is written from JS but contents probably come from database. So here is my solution :
// Native JS version
// Working Fiddle : https://jsfiddle.net/d34cbtw7/
var togglers = document.querySelectorAll('[data-toggle="tab"]');
for (var i = 0; i < togglers.length; i++) {
togglers[i].addEventListener('click', function() {
var tabs = document.querySelectorAll('.tab');
for(var j = 0; j < tabs.length; j++) {
tabs[j].classList.remove('active');
}
var $target = document.querySelector(this.getAttribute('data-target'));
$target.classList.add('active');
});
}
// jQuery version
$('body').on('click', '[data-toggle="tab"]', function(e) {
e.preventDefault();
// Select our target
var $target = $($(this).data('target'));
// Hide all tabs
$('.tab-contents .tab').removeClass('active');
// Show only $target tab
$target.addClass('active');
});
.tab-contents .tab {
display: none;
}
.tab-contents .tab.active {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button data-toggle="tab" data-target="#cat-A-content">
Cat A
</button>
<button data-toggle="tab" data-target="#cat-B-content">
Cat B
</button>
<div class="tab-contents">
<div class="tab active" id="cat-A-content">
My category A contents
</div>
<div class="tab" id="cat-B-content">
My category B contents
</div>
</div>
I also don't really like "onclick" attribute in HTML...
I've made a quick codepen as example.
You can achieve this by passing a parameter to the onClick function. In this example I keep track of the last button clicked, and the text it should render. If the last button clicked was the same button, the switched back to default. I hope this helps.
https://codepen.io/maffekill/pen/rbpjzw
HTML
<p>Click the button to swap the text of the DIV element:</p>
<p><button onclick="myFunction(1, 'TEXT A')">TEXT A</button></p>
<p><button onclick="myFunction(2, 'TEXT B')">TEXT B</button></p>
<div id="myDIV">Default Text</div>
JS
// Keep track of the button currently clicked
var activeBtn = null;
function myFunction(btnId, text) {
var x = document.getElementById("myDIV");
// If the last button is the same as the new one, show default text
if (activeBtn === btnId) {
x.innerHTML = "Default Text";
activeBtn = null
} else {
// Else show the text given to the text param
x.innerHTML = text;
activeBtn = btnId;
}
}
There are multiple ways to achieve this, but the easiest way I could come up with to explain this to you would be as following:
function myFunction(myEle) {
var x = document.getElementById("myDIV");
x.innerHTML = "This is category " + myEle.value;
}
<p>Click the button to swap the text of the DIV element:</p>
<p>
<button onclick="myFunction(this)" value="a">
Category A
</button>
<button onclick="myFunction(this)" value="b">
Category B
</button>
</p>
<div id="myDIV">Hello</div>
JSFiddle
No need to overcomplicate things.
Firstly you would like to send the clicked element from the caller (which in this case would be the clicked element as well, the <button> element). You could use JavaScript's thisfor this purpose.
Within your function you can name a parameter between parenthesis, so in my example above: function myFunction() contains a parameter called myEle so it will look like: function myFunction(myEle). Once the function will be triggered, the parameter called myEle will be set to the clicked element (or
JavaScript's this). You can simply access any of its attributes like value by using a dot: myEle.value.
Knowing the above, you could apply it to whatever you require your function to do (refer to my example code above).

React link OnClick prevent href

I have a link with a href and onClick function.
<a href={menuItem.url} onClick={e => {
var checkedItems = document.querySelectorAll("input:checked") as NodeListOf<HTMLInputElement>;
for (let i = 0; checkedItems[i]; i++) {
checkedItems[i].checked = false;
}
window.location.href = menuItem.url; }}>
I want the href link the be there so users can see the url when hovering, but I want only the onclick to be executed.
I have tried
e.stopPropagation();
e.preventDefault();
e.nativeEvent.stopImmediatePropagation();
return false;
But none of those seem to work.
One solution would be to add a new "internal element" such as a <span> inside of the <a> element, and bind the onClick event handler with event.stopPropagation() to that internal element.
This would cause the <span> to intercept and stop the propagation of the click event before it bubbles up to the parent <a> (which would by default cause the browser to navigate to the href url).
You should find this method still preserves visibility of the url assigned to the href attribute of <a> for the user when it is hovered with the mouse cursor:
<a href={menuItem.url}>
<span onClick={e => {
// Intercepts and prevents the event from propagating up to the parent <a>
e.stopPropagation();
// Your existing logic for the onClick event
var checkedItems = document.querySelectorAll("input:checked") as NodeListOf<HTMLInputElement>;
for (let i = 0; checkedItems[i]; i++) {
checkedItems[i].checked = false;
}
window.location.href = menuItem.url;
}}> The text label for your link </span>
</a>
For this method to work, it assumed that there is no padding between the box border (outer boundary) of the <a> and the box border of the inner <span>. Here's a jsFiddle (non-jsx) demonstrating the general idea.
Hope that helps!

Pivot icons are not clickable

I have a pivot element in my page, it's work but when I want to change text by icons, they become not clickable and we have to click in the grey part. Do you know how make them clickable ?
In green the clickable part and in red not clickable part.
Part of my code :
<li id="listPivotAccount" class="ms-Pivot-link is-selected " data-content="account" title="Mon compte" tabindex="1">
<i style="" class=" ms-Icon ms-Icon--Accounts" aria-hidden="true"></i>
</li>
You can check the code here
For the record, I have never used SharePoint, so there may be a more elegant solution.
You can fix this behaviour by adding this vanilla JavaScript after your current JavaScript:
// select all icons
var msIcons = document.querySelectorAll(".ms-Icon");
// loop all icons
for (var i = 0; i < msIcons.length; i++) {
// add a click event to the nearest element with class "ms-Pivot-link"
msIcons[i].closest(".ms-Pivot-link").addEventListener("click", function() {
this.click();
});
}
jQuery Example of the above code:
$(".ms-Icon").on("click", function() {
$(this).closest(".ms-Pivot-link").click();
});
var Dropdown = new Class({
initialize: function() {
var e = this;
document.addEvents({
"click:relay(.windowLabel, .dropdown a.dropdownTrigger)": function(t, n) {
t && (t.preventDefault(),
t.stopPropagation()), // issue is here
e.showPopover.call(e, n)
}
}),
document.body.addEventListener("click", function(t) {
e.hideOutside.call(e, t)
})
},
// ...
})
Problem is in preventing propagation of events, and as result all nested elements shouldn't emit what you need.
What is the solution?
You can try add the icon in different way (for example using :before, :after)
The simple way to fix it is to trigger the pivot with a click. So if you use JQuery :
$('.ms-Icon').click(function () {
var pivot = $(this).closest(".ms-Pivot-link");
pivot.click();
});
Short and compatible with IE > 9