I have the following
<custom-component>
<div slot="form">
<form>
bunch of input elements
<span slot="inner"> <button type="submit" id="submitButton"></button> </span>
<button type="Reset"></button>
</form>
</div>
</custom-component>
How do I listen to on click event the button type submit from the parent component custom-component?
For reasons that I'm not going to to into, making the whole slotted form a component is not an option.
In the custom component file, this is what I tried
<slot name='form' >
<slot name='inner' #click=${this.handleSearch}> </slot>
</slot>
It seems like you'll need to change a few things to make this work as intended.
First, the "inner" slot in your example isn't doing anything. You can't have a nested slot inside of light DOM rendered slot children. To test this out to see that this is true, you can try two things.
Remove the <slot name="inner"> element, and you'll see that the "Submit" button still renders just fine in HTML.
Second, the #click listener on the <slot name="inner"> never fires. Try moving it to the <slot name="form"> and you'll see that the callback fires as expected.
Another issue you'll run into is that the default browser Event being bubbled through the shadowRoot doesn't contain any information. If you console.log the data in the event callback, you'll see that all you get is {isTrusted: true} instead of the more useful event and element information.
Since you can't create the form within the web component, the other option is to just handle all of the logic with JavaScript in a <script> tag next to your <custom-component> in HTML. This makes sense since this is closer to the intended use of slotted children - to be able to add arbitrary HTML as children to your web components, but your web component isn't necessarily in charge of anything related to that HTML, it's just rendering it.
If you need to be in charge of the rendering, events, or logic related to the HTML children, they should be inside the web component, or else the logic needs to be handled outside of the web component.
Related
For a research project, I am building a web tool to track users' interactions with specific elements that I need for later analysis.
Often, I have interactable containers that contain graphics or text elements, sometimes both. For instance:
<div class="interaction-field trackable" id="specific-interaction-id"> <!-- This id is what I want to track -->
<img id="img-id" src="path/to/img.png"/> <!-- But i am triggering either this... -->
<p> Some Text </p> <!--...or this -->
</div>
I style the container by its interaction-field class and have a javascript function that logs all interactions based on the trackable class. Now, I got two issues.
For the tracking, I want to store the container's id for simplicity, as in such cases the <img> and <p> belong together. However, most click events, for example, are only recognised on child elements.
Because of this, the parents' targetable class is not triggering, denying any logging. Since I want the parents' id as a compound trackable element, I would like to avoid giving the children the targetable class to avoid ambiguity and redundancy.
I do get the general layering problem and it is logical that I rather hit the children than their parents. But is there an elegant way to always pass the parents' classes and id's to make the logging easier? Or is there an even simpler solution that I am not seeing?
Thanks in advance!
You can delegate the click handler to the document. Then grab the clicked element and use closest to find the parent element's ID if it has the trackable class.
document.addEventListener("click",(e)=>{
let trackedEl = e.target.closest(".trackable");
if(trackedEl){
console.log(trackedEl.id)
}
});
<div class="interaction-field trackable" id="specific-interaction-id"> <!-- This id is what I want to track -->
<img id="img-id" src="path/to/img.png"/> <!-- But i am triggering either this... -->
<p> Some Text </p> <!--...or this -->
</div>
First of all, if your question is on javascript you should have showed us the corresponding code.
Now, if you have event listeners on parent and child elements you can handle this through propagation.
When an event is triggered there are three phases.
Capturing - It first goes up the tree of elements starting from the root up to the targeted element. This by default doesn't trigger anything
Target phase - It triggers the targeted element's event.
Bubbling - It goes back down the tree again all the way to the root. This phase does trigger the events of the parents.
Your parent's event should be triggering in the bubbling phase. First make sure
that you set it correctly. Make it trigger an alert or something that makes you know if its happening.
You can also decide to trigger it in the capturing phase but I don't you need that.
Once you know for sure the event works it should be triggered when you click on its children. From there it is only a matter of adding the id to your list.
You can check this page to learn more about event propagation:
https://javascript.info/bubbling-and-capturing
I think you might find exactly what you need in there.
I created a couple of reusable components with a slot within it. So I can manage the content, style, or whatever it is anytime I call it, in other components. I wonder, can I passing an event handler to
those components but inside the template tag?
ReusableComponent
<a :href="hrefProps"> // I want the handler goes here
<slot></slot> // it will render plain text, without html tags
</a>
Main Component
<reusable-component>
<template #click="sayHelloWorld">Hello World!</template> // didn't work
</reusable-component>
How can I make that to work? Should I wrap them into at least 1 tag, like
<template><a #click="sayHelloWorld"></a></template> // sure it will working
Template tags don’t create a DOM element, so pi can’t add a listener to them, or add a class or anything else.
They are just a semantic tool to wrap multiple children in a loop.
Add the listener to the real parent element i-e href tag
I am still new to Angular and I'm struggling to get the DOM Element of an Angular Click Listener.
What I have is the following component HTML:
<div *ngFor="let menuItem of menu; index as itemId" class="menuItem">
<div class="menuItem__top" (click)="itemClicked($event, !!menuItem.submenu)">
<!-- Additional divs inside... -->
</div>
</div>
I would like to toggle a class of "menuItem__top" when it is clicked. My approach was to use a click event listener but I can't get the source Element to apply the class on.
itemClicked(event, hasSubmenu){
console.log(this) //is the component
let target = event.target || event.srcElement || event.currentTarget;
if(hasSubmenu){
console.log(target);
}
}
It could be done by getting the target of the $event but this way I would need to check the target and move with closest(".menuItem__top") up to the correct source element.
Is there an easy way in Angular to pass the source element of the click listener to the click function or a way to get it inside the click function?
In vanilla JS it would be as easy as using "this" inside the click function, but in Angular this is bind to the component. (In this case, it would be ok to loose the binding to the component if this is the only way.)
I thought about two ways:
Assigning a dynamic reference containing some string and the itemId, passing the itemId and retrieving the reference object based on the itemId in the listener.
Using a #HostListener to listen on every "menuItem__top" click and toggling the class every time.
What do you think is the best way? I feel like I am missing something simple here.
Go the other way around. People are used to jQuery and the way it works (selecting elements already present in the DOM, then adding them a class). So in Angular, they try to do the same thing and grab the element that was clicked, to apply it a class. Angular (and React and others) work the other way around. They construct the DOM from data. If you want to modify an element, start with modifying the data it was generated from.
This <div class="menuItem__top"> that you click on is constructed from let menuItem. All you have to do is add a property, say "isClicked" to menuItem.
<div *ngFor="let menuItem of menu; index as itemId" class="menuItem">
<div class="menuItem__top"
[class.isClicked]="menuItem.isClicked"
(click)="menuItem.isClicked = true">
<!-- Additional divs inside... -->
</div>
</div>
I have the template like this
<template>
<div>
<div id="hiddenElement">
<MyElement v-for='...' #click="...">
</MyElement>
</div>
<div id="appendElementsHere" />
</div<
</template>
The user can append the element into the list, so I have some function like this:
someFunc(){
const hidden = document.querySelector('#hiddenElement')
const target = document.querySelector('#appendElementsHere')
target.innerHtml += hidden.outerHtml
}
The element is cloned can append to the #appendElementsHere successfully,
but the click function is not working. I think that maybe the click function in the vue element, not the html. How can I clone the element as vue-element, not html only? Or any idea to create vue element in the script (method) and then append to the dom ??
What you're doing is technically correct from a javascript perspective, however the click function doesn't work because you're not doing any data bindings after you "clone" the elements. I say "clone", because what you're doing is just passing a bunch of strings containing HTML. With that said, what you will have to do next is to add the event listeners to the cloned elements manually as well.
However, you could try to do it in a more Vue way, so instead of having a hidden component waiting to be cloned, you could create the Vue instance of the component you want (MyElement in your case) with code passing all the props/data you want and then append it to the element where you need it.
Here's an example with how to do it with buttons. If click on "Click to insert" you'll see how a CustomButton component gets appended to the right of the button.
Thanks,
In Polymer 1.1, this is my index.html
<template is="dom-bind" id="app">
<paper-drawer-panel force-narrow id="the-drawer">
<paper-header-panel drawer>
<paper-toolbar></paper-toolbar>
<div> Drawer content... </div>
</paper-header-panel>
<paper-header-panel shadow="true"
mode="waterfall-tall"
main
class="fit">
<paper-toolbar>
I would like to add iron-resizable-behavior to catch resize events. The thing is, I don't want to make a custom element to do it. Is it possible to add iron-resizable-behavior to index.html without making a custom element?
If you check the Behaviors guide, it mentions that the whole purpose for behaviors is for them to be extended by custom elements, so you can't use them without extending them in an element.
You could either create a simple element which extends iron-resizeable-behavior and fires events you can listen to or just make that entire dom-bind into an element.
Then again, depending on what you need to do, using iron-media-query might be enough.