How to make event stop propagating in React - javascript

First off I did look at all the related answers but none solved my problem. I have the following:
class TweetingBox extends React.Component{
render(){
return (
<div id="page-blackout" onClick={this.props.closeTweetingInterface}>
<div id="tweeting-box" onClick={(proxy) => {proxy.stopPropogation()}}>
<h5 id="tweeting-box-header">Compose new Tweet</h5>
<TweetingInterface initialContent={this.props.initialContent}/>
</div>
</div>
)
}
}
div with id="page-blackout" covers the entire page and div with id="tweeting-box" is centered on the page, containing a form in the component TweetingInterface. When I click anywhere, both in the form as well as on tweeting-box itself, this.props.closeTweetingInterface is called. How can I stop this?

All the sibling elements will get their parents property, so the parent function will get called closeTweetingInterface every time the parent or the sibling elements get called on.

Related

Web Components - Multiple elements per slot

I'm working on new web-components and ran into some issues concerning slots.
I have an outer container-component with a "main-slot" inside, in which multiple elements should be inserted (into the same slot). However, it is only possible to add one element per named slot.
My question: is there a way to add multiple elements to one named slot? Like shown here:
<own-container>
<own-element slot="main"></own-element>
<own-element slot="main"></own-element>
<own-element slot="main"></own-element>
<own-element slot="main"></own-element>
</own-container>
There is also imperative <slot>
super().attachShadow({
mode: 'open',
slotAssignment: 'manual' // imperative assign only
})
But! you get Named slots OR Imperative slots, on one Web Component
To mimic named <slot>, assigning content to the same named <slot>
you probably need the Mutation Observer API
addendum
You can have multiple elements per slot:
<component-with-slots>
<H1 slot="title">Web Components are great!</H1>
<h2 slot="title">A bit hard to master</h2>
<b slot="title">But Great!</b>
</component-with-slots>
<script>
customElements.define('component-with-slots', class extends HTMLElement {
constructor() {
super()
.attachShadow({mode:'open'})
.innerHTML="<slot name='title'></slot>";
}
});
</script>
Nope. It is not possible for named slot. The trick is to have a wrapper div element to wrap your lightDOM.
<own-container>
<div slot="main">
<own-element></own-element>
<own-element></own-element>
<own-element></own-element>
<own-element></own-element>
</div>
</own-container>
If the presence of additional div causes styling problem, then you can use new contents type of display box.
div {
display: contents;
}
The display: contents causes an element's children to appear as if they were direct children of the element's parent, ignoring the element itself. However, note that it can cause accessibility issues.

Nested HTML element not assigning to slot

In the below code I am trying to assign <span slot='test-slot'>b</span> to <slot name='test-slot'>a</slot> but the assignment does not work. If I bring <span slot='test-slot'>b</span> outside of its parent <div> container the assignment does take place as expected.
Why is this? Is there anyway you can assign from nested elements with the slot element? If not, any alternatives? This is obviously a reduced test case but in my real web component, it is much more intuitive for a user to add an element with the slot tag within other containers.
<test-element>
<div>
<span slot='test-slot'>b</span>
</div>
</test-element>
<template id='template-test-element'>
<slot name='test-slot'>non slotted content</slot>
</template>
<script>
class TestElement extends HTMLElement {
constructor() {
let template = document.getElementById("template-test-element")
.content.cloneNode(true);
// Initialise shadow root and attach table template
super() // sets AND return 'this' scope
.attachShadow({mode:"open"}) // sets AND returns shadowRoot
.append(template);
}
}
customElements.define('test-element', TestElement);
</script>
Named slots only accept top-level children that have a matching slot-attribute.
See this (old) Polymer explainer or this more recent article.
Edit: Not sure where this is coming from though, the spec fails to mention this requirement: https://html.spec.whatwg.org/multipage/dom.html#attr-slot
It also is neither mentioned here nor here.

Emit not registering in VueJS?

I was trying to make a simple function in the parent element that updates a variable when it gets an emit from the child element. I've been stuck on it for 2 hours and I'm fed up because I know that the answer will be so simple.
Code in question:
Child element:
<div v-on:click="$emit('event1')">
Click
</div>
Parent element:
<p #event1="test()"></p>
<Section /> // the child element
methods: {
test() {
console.log("test")
},
}
The code above doesn't log anything. I've made it as minimal because no outside factor would change the outcome of the code.
Thanks in advance!
The event listener has to be attached to your <Section /> element, event1 is not emitted globally

Wrap div not rendering in React

I have a render() function that returns a div with content inside of it, such as:
return(
<div style={{background: "black"}}>
<[ReactComponent]>
<[AnotherReactComponent]>
...
</[AnotherReactComponent]>
</[ReactComponent]>
</div>
);
When I inspect the element, the outer div does not render, however the ReactComponent does.
maybe you should add some construction like this in the render method of RComponent
<View>{props.children}</View>

Passing only clicked element to onClick function - reactjs

I have multiple elements with same className and i want if some element (with className history-node) is clicked it should get className active along with current className.
But i am having an issue, there are childs of that element and if child elements gets clicked they also get class Active.
Here is the code:
<div className="history-node-container" key={index}>
<div className="history-node" onClick={(e) => {this.handleHistoryClick(e.target)}}>
<span className="history-title">{heading.action}</span>
<span className="history-date">{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
</div>
</div>
handleHistoryClick function
handleHistoryClick(target){
$('.history-node').removeClass('active'); //removing active class from all elements
target.className = 'history-node active';
}
I want to run function when user click on element with className history-node
But if user clicks on history-title, ClickHandler gives class active to history-title element.
EXPECTED BEHAVIOUR: if history-node is clicked only history-node should get class active.
One tip and how to solve your problem (In two ways)
Tip: Not usually the best idea to mix React with jQuery. React came in as a major paradigm shift in how we interact with the DOM. Try to read a little bit more about React, how it works, why is it so different from simply adding/removing elements in the DOM with jQ.
Some references to help you with that:
How to go from jQuery to React.js?
Thinking in React for jQuery Programmers
Now, back to your question
You should use currentTarget.
As the .history-title and .history-date elements are wrapped within .history-node, any click on them will trigger it's parent's event, since .history-node body is .history-title + .history-date. That's the correct behavior. When you trigger an event in JS, the event object receives two parameters: target, which is the event triggering the event and currentTarget, the element that is actually listening for the event.
Now for the code:
with JQ
Component:
<div className="history-node-container" key={index}>
<div className="history-node" onClick={handleHistoryClick}>
<span className="history-title">{heading.action}</span>
<span className="history-date">{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
</div>
</div>
Click:
handleHistoryClick(event){
$('.history-node').removeClass('active')
event.currentTarget.classList.add('active')
}
The React way (Pure React, no modules)
Component:
class HistoryNode extends Component {
constructor(props) {
super(props)
this.state = { isActive: false }
this.handleClick = this.handleClick.bind(this)
}
handleClick(e) {
let state = this.state
this.setState({isActive: !state.isActive})
}
render() {
return(
<div className="history-node-container">
<div className={`history-node ${this.state.isActive ? 'active' : ''}`} onClick={handleHistoryClick}>
<span className="history-title">{heading.action}</span>
<span className="history-date">
{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
</div>
</div>
)
}
}
Notice how you don't need to manipulate the DOM at any moment at the React solution. You just attach your event to that particular component, define how it's state change should reflect on the UI and let React do the rest.
Hope it helps ;)
Reference for target vs currentTarget
I think the event propagates to child components.
Have you tried this ?
<div className="history-node-container" key={index}>
<div className="history-node" onClick={handleHistoryClick}>
<span className="history-title">{heading.action}</span>
<span className="history-date">{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
</div>
</div>
HandleClick function
handleHistoryClick(event){
event.stopPropagation();
$('.history-node').removeClass('active');
event.target.className = 'history-node active';
}
EDIT : You could make it simpler though (and without jQuery) using your component state. But without knowing how you wrote your component I cannot give you a snippet illustrating it. Be careful too as you interact directly with the DOM, this implies a performance loss. Using the React state allows you to avoid such thing!

Categories

Resources