Pass event through to Aurelia custom element - javascript

I have a Call To Action (CTA) which has a ripple effect, provided by a custom element called click-ripple, similar to Google Material Design. The custom element called click-ripple has a rule in the CSS to prevent this element from being clickable:
pointer-events: none;
If I do not add this rule, the element will be on top of its parent and it will not link the user through to the correct page or it will not perform the right action. Is there a way to feed an event from the parent through to one of its children without too much hassle?
EDIT
Let's say there is a button made up of a anchor-tag on the page. The markup of that anchor tag would look like this:
<template>
<a href="https://www.google.com">
<click-ripple></click-ripple>
</a>
</template>
My question is: what is an efficient way to feed a click action from the anchor tag forward to the click-ripple element?

The answer is to add an event listener to the parent of the current element. Looking it up in the W3 specification I came to the conclusion that I need to use element.parentElement.
clickripple.js
//Shortened for everyone's sanity
export class ClickRipple {
constructor(CssAnimator, Element) {
this.animator = animator;
this.element = element;
}
attached() {
this.element.parentElement.addEventListener("click", (event) => {
this.playAnimation(event);
});
}
playAnimation(event) {
//Math based on event
//Animation with Aurelia's CssAnimator
}
}

A common way of adding behavior (such as a click ripple) to an element is to use a custom attribute: <parent click-ripple></parent>.
Otherwise here's how you could pass the events to an arbitrary element:
click-through-custom-attribute.js
import {inject} from 'aurelia-framework';
#inject(Element)
export class ClickThroughCustomAttribute {
constructor(element) {
this.element = element;
this.handler = event => this.value.dispatchEvent(event);
}
attached() {
this.element.addEventListener('click', this.handler);
}
detached() {
this.element.removeEventListener('click', this.handler);
}
}
<require from="click-through-custom-attribute"></require>
<button ref="button1>I'm Behind</button>
<button click-through.bind="button1">I'm In Front</button>
If you know that the element you want to pass the click to is always going to be the parent element you could do something like this inside of your click-ripple custom element:
import {inject} from 'aurelia-framework';
#inject(Element)
export class ClickRipple {
constructor(element) {
this.element = element;
this.handler = event => this.element.parentElement.dispatchEvent(event);
}
attached() {
this.element.addEventListener('click', this.handler);
... add click ripple behavior ...
}
detached() {
... remove click ripple behavior ...
this.element.removeEventListener('click', this.handler);
}
}

Related

Simple way to detect click outside of child component

I want to expand and contract child div component depending on the value of a variable, but I want to be able to click out of that component (in the parent of the sibling) as well as collapsing it.
Here's an stackblitz example. I have attempted to use HostListener from what I found in this question, but it did not help my case.
#HostListener('document:click', ['$event'])
documentClick(event: MouseEvent) {
// your click logic
}
Objectives:
When I click on the child (hello) component, I want it to expand if it is not already and contract if it is already expanded
When I click on anything else (ie. parent or sibling component), I want the child (hello) component to contract if it is expanded.
I do not want the child (hello) component to expand when clicking in the parent/sibling.
Update: Using HostListener
hello.component.html
<div class="box" (click)="clicked()" [ngClass]="{large: isEnlarged}">Hello.component</div>
hello.component.ts
export class HelloComponent {
isEnlarged = false;
clicked() {
this.isEnlarged = !this.isEnlarged;
}
#HostListener('document:click', ['$event'])
documentClick(event: MouseEvent) {
console.log('clicked');
this.isEnlarged = false;
}
}
app.component
export class AppComponent {
}
The problem is your click handler is setting expanded to true before document click event handler sets it to false, so it's always false.
You could only set it to false if the event target is not your component:
https://stackblitz.com/edit/mouse-click-anywhere-8bwg6p?file=src/app/hello.component.ts
#HostListener('document:click', ['$event'])
documentClick(event: MouseEvent) {
console.log('clicked');
console.log(event);
if (event.target.id !== 'box') {
this.isEnlarged = false;
}
}

How to remove particular class name from an selected element in VueJs?

This is what my vuejs methods looks like. Here in the changeRoute function I can change class name by e.target.className = 'clicked'; But when I try to remove that class name from other elements I cant do it by pre.removeClass('clicked'); How can I accomplish this ?
<script>
export default {
components: {
},
data() {
return {
}
},
methods: {
changeRoute(e, route) {
var pre = this.$el.querySelector('.clicked');
if(pre) {
// pre.removeClass('clicked');
}
this.$router.push({
name: route
});
e.target.className = 'clicked';
}
},
mounted() {
this.$nextTick(() => {
})
}
}
</script>
Also how can push class name rather than replace all by e.target.className = 'clicked';
You can use classList.add and classList.remove for this.
But it seems like you want to style link depending on the current route which can be done with vue-router as it adds class on the links matching the current route.
Vue-router active class
If the selected element is part of a li list and class should be added (and consequently removed from previous clicked li), here's a good solution by Herteby on the VueJS Forum
https://forum.vuejs.org/t/how-to-add-active-class-to-element/28108/2
Use actual Vue to accomplish this task.
I could explain here how but the Vue page does it quite well.
https://v2.vuejs.org/v2/guide/class-and-style.html

How to remove element when clicking outside the element in javascript?

I have created a dropdown menu for my web app and when clicking on a icon i'm opening my dropdown menu. I want to remove the dropdown menu when i click anywhere other than the dropdown menu. My current approach removes the element when clicking outside the element. But i cannot open the dropdown menu when clicking on the icon after that. How can i fix it?
class DropDown extends Component {
constructor(props) {
super(props);
this.myFunction = this.myFunction.bind(this);
this.state = {
openmenu:false
}
};
myFunction(e) {
e.stopPropagation();
this.setState({
openmenu: !this.state.openmenu
})
render() {
window.onclick = function (event) {
if (!event.target.matches('myDropdown')) {
document.getElementById('myDropdown2').remove();
}
}
return (
<div className="dropdown small_font" id="myDropdown" >
<i className="fa fa-cog user_settings_icon" style={{marginTop: '6px'}} onClick={this.myFunction}
aria-hidden="true"></i>
{this.state.openmenu?
<div className="dropdown-content" id="myDropdown2">
<a className="dropdown_item"><i className="fa fa-trash-o margin_right10px" aria-hidden="true"></i>Delete</a>
<a className="dropdown_item"><i className="fa fa-flag-o margin_right10px" aria-hidden="true"></i>Report</a>
</div>:null
}
</div>
);
}
}
The error i'm getting when clicking on the icon for the second time
Uncaught DOMException: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
If you don't want to keep track of adding and removing click events on unmount and a solution that works across all browsers id recommend using a library. Ive used https://github.com/Pomax/react-onclickoutside and it works very well, heres a snippet.
import React, { Component } from "react";
import onClickOutside from "react-onclickoutside";
class MyComponent extends Component {
handleClickOutside = evt => {
// ..handling code goes here...
};
}
export default onClickOutside(MyComponent);
If you dont want to use a library use the onBlur
class MyComponent extends Component {
handleClickOutside = evt => {
// ..handling code goes here...
};
render(){
<div onBlur={this.handleClickOutside}>
<SomeChild />
</div>
}
}
Lastly your using React wrong, your using it as if it was jquery which it is not. you dont remove anything manually. You have state that you update when to close the dropdown and when to open it.
Using events such as window.onclick in render method is not a good practice since every time you update your state this event is going to be instantiated.
I also noticed you have created the state prop openmenu and myFunction but you not using them properly.
My suggestion is to attach DOM events in ReactJS's lifecycle events:
componentDidMount() {
window.addEventListener('onClick', this.myFunction)
}
componentWillUnmount() {
// Make sure to remove such event when your component is unmounted
window.removeEventListener('onClick', this.myFunction)
}

Angular2 remove click event binding after first click

In my application I have a button with a click even on it:
<button class="btn btn-default" (click)="doSomething()">
From within the doSomething method, is there any way to remove the (click) event from the button (so the user can't trigger the functionnality anymore?).
I tried to set a disabled prop on the button but it doesn't change Angular2 behavior.
I tryed to use (click)="doSomething($event) and then
doSomething($event) {
// My method logic goes here
...
...
console.log('Method Logic');
//Attempt to overwrite click event
let target = event.target || event.srcElement || event.currentTarget;
this.renderer.listen(target, 'click', (event) => {
console.log('clic block');
});
}
But It doesn't "replace" the click event. So after that, on click, both original logic and the "click block" console log are triggered!
Method 1:
You can set a boolean variable, so if the user calls the function, boolean value changes and the user will be able to call the function on click again but actually nothing will happen.
bool: boolean = true;
doSomething($event) {
if (this.bool) {
// My method logic goes here
...
...
console.log('Method Logic');
this.bool = false;
}
}
Method 2:
You can add a condition to your html component, if specified variable (in this case bool) is true, the function is going to be executed, but only once because the bool variable will be set to false and the click function will execute nothing (null) from now on.
bool: boolean = true;
doSomething($event) {
// My method logic goes here
...
...
console.log('Method Logic');
this.bool = false;
}
(click)="bool ? doSomething($event) : null"
The downside of just adding a guard variable for the execution is that the actual event listener is still in place and will trigger Angular's change detection when clicked, even though it doesn't do anything.
To actually remove the event listener, you have to add it via the component's Renderer. This will return a function that removes the event listener when called:
import {Component, AfterViewInit, Renderer, ViewChild, ElementRef} from '#angular/core';
#Component({
template: `<button #button>...</button>`
})
export class SampleComponent implements AfterViewInit {
#ViewChild('button') button: ElementRef;
private cancelClick: Function;
constructor(private renderer: Renderer) {}
ngAfterViewInit() {
this.cancelClick = this.renderer.listen(this.button.nativeElement, 'click',
($event: any) => this.handleClick($event));
}
handleClick($event: any) {
this.cancelClick();
// ...
}
}
If your goal is to just remove the event listener when the event is fired for the first time, this can be implemented as a plugin to Angular's event system. I added it as a part of my ng2-events utility library [source], which lets you now do the following:
<button (once.click)="handleClick($event)">...</button>
For people dealing with Observable / Subject for handling some events :
<button (click)="clickEvents$.next($event)">
class MyComponent {
clickEvents$ = new Subject<MouseEvent>();
firstClick$ = this.clickEvents.take(1); // Observable of first click
}

Close/hide an element when clicking outside of it (but not inside)

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>
</>
);
};

Categories

Resources