I have the following files:
Test.js:
import React from 'react';
import style from './style.module.css'
function Test() {
return(<div className={style.classcolor}>Test</div>);
}
export default Test;
style.module.css
.classcolor
{
background-color:blue;
}
Is there a way to change the css attributes inside the js file, similar to the code below?
style.classcolor.backgroundColor ="red";
An option is to use a variable and change the class depending on it:
class Test extends React.Component {
constructor(props) {
super(props);
this.state = { orange: true }
}
toggleClass = () => {
this.setState({ orange: !this.state.orange })
}
render() {
const { orange } = this.state
return (
<div>
<div className={orange ? 'orange' : 'red'}>Test</div>
<button onClick={this.toggleClass}>Toggle class</button>
</div>
);
}
}
ReactDOM.render(
<Test />,
document.getElementById("react")
);
.orange{
color: orange;
}
.red{
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
A not really react way is to use element.style, more information:
document.getElementById('test').style.color = 'orange';
<div id="test">Test</div>
you can add array [0] after get the className, and then you can access the style from DOM
document.getElementsByClassName(style.classcolor)[0].style.backgroundColor = 'red'
You can see this library styled-component, you can pass props for the component and by the value of the prop, you decide in how the style will be.
Related
I'm trying to create a BaseOverlay component that basically teleports its content to a certain area of my application. It works just fine except there's an issue when using it with v-show... I think because my component's root is a Teleport that v-show won't work because Teleport is a template.
I figured I could then use inheritAttrs: false and v-bind="$attrs" on the inner content... this throws a warning from Vue saying Runtime directive used on component with non-element root node. The directives will not function as intended. It results in v-show not working on MyComponent, but v-if does work.
Any clues as to what I'm doing wrong?
Example
App.vue
<script setup>
import MyComponent from "./MyComponent.vue";
import {ref} from "vue";
const showOverlay = ref(false);
function onClickButton() {
showOverlay.value = !showOverlay.value;
}
</script>
<template>
<button #click="onClickButton">
Toggle Showing
</button>
<div id="overlays" />
<div>
Hello World
</div>
<MyComponent v-show="showOverlay" text="Doesn't work" />
<MyComponent v-if="showOverlay" text="Works" />
</template>
BaseOverlay.vue
<template>
<Teleport to="#overlays">
<div
class="overlay-container"
v-bind="$attrs"
>
<slot />
</div>
</Teleport>
</template>
<script>
export default {
name: "BaseOverlay",
inheritAttrs: false,
};
</script>
MyComponent.vue
<template>
<BaseOverlay>
{{text}}
</BaseOverlay>
</template>
<script>
import BaseOverlay from "./BaseOverlay.vue";
export default {
name: "MyComponent",
components: {
BaseOverlay
},
props: {
text: {
type: String,
default: ""
}
}
}
</script>
I would consider moving the modal/overlay dependency out of the component and into the app composition to make it more reusable.
Note the isMounted check check - this is to add a delay if the outlet containers have not yet been defined. You may need to add additional handling if your outlets are not pressent on mount e.g. <div id="outlet" v-if="false">
const {
createApp,
defineComponent,
ref,
onMounted
} = Vue
//
// Modal
//
const MyModal = defineComponent({
name: 'MyModal',
props: {
to: {
type: String,
required: true
},
show: Boolean
},
setup(){
const isMounted = ref(false);
onMounted(()=> isMounted.value = true )
return { isMounted }
},
template: `
<teleport :to="to" v-if="isMounted">
<div class="overlay-container" v-show="show">
<slot />
</div>
</teleport>
`
})
//
// Component
//
const MyComp = defineComponent({
name: 'MyComp',
template: `<div>Component</div>`
})
//
// App
//
const MyApp = defineComponent({
name: 'MyApp',
components: {
MyModal,
MyComp
},
setup() {
const modalShow = ref(false);
const modalOutlet = ref('#outletOne');
const toggleModalShow = () => {
modalShow.value = !modalShow.value
}
const toggleModalOutlet = () => {
modalOutlet.value = modalOutlet.value == '#outletOne'
? '#outletTwo'
: '#outletOne'
}
return {
toggleModalShow,
modalShow,
toggleModalOutlet,
modalOutlet,
}
},
template: `
<div>
<button #click="toggleModalShow">{{ modalShow ? 'Hide' : 'Show' }} Modal</button>
<button #click="toggleModalOutlet">Toggle Modal Outlet {{ modalOutlet }} </button>
</div>
<MyModal :to="modalOutlet" :show="modalShow">
<MyComp />
</MyModal>
<div id="outletOne">
<h2>Outlet One</h2>
<!-- outlet one -->
</div>
<div id="outletTwo">
<h2>Outlet Two</h2>
<!-- outlet two -->
</div>
`
})
//
// Assemble
//
const app = createApp(MyApp)
app.mount('body')
/* just some styling */
#outletOne { color: tomato; }
#outletTwo { color: olive; }
h2 { margin: 0; }
[id*=outlet]{ display: inline-flex; flex-direction: column; padding: 1rem; }
button { margin: 1rem; }
<script src="https://unpkg.com/vue#3.1.4/dist/vue.global.js"></script>
Wanted to follow-up on this. I started running into a lot of other issues with Teleport, like the inability to use it as a root element in a component (like a Dialog component because I want to teleport all dialogs to a certain area of the application), and some other strange issues with KeepAlive.
I ended up rolling my own WebComponent and using that instead. I have an OverlayManager WebComponent that is used within the BaseOverlay component, and every time a BaseOverlay is mounted, it adds itself to the OverlayManager.
Example
OverlayManager.js
export class OverlayManager extends HTMLElement {
constructor() {
super();
this.classList.add("absolute", "top-100", "left-0")
document.body.appendChild(this);
}
add(element) {
this.appendChild(element);
}
remove(element) {
this.removeChild(element);
}
}
customElements.define("overlay-manager", OverlayManager);
BaseOverlay.vue
<template>
<div class="overlay-container" ref="rootEl">
<slot />
</div>
</template>
<script>
import {ref, onMounted, onBeforeUnmount, inject} from "vue";
export default {
name: "BaseOverlay",
setup() {
const rootEl = ref(null);
const OverlayManager = inject("OverlayManager");
onMounted(() => {
OverlayManager.add(rootEl.value);
});
onBeforeUnmount(() => {
OverlayManager.remove(rootEl.value);
});
return {
rootEl
}
}
};
</script>
App.vue
<script setup>
import {OverlayManager} from "./OverlayManager.js";
import MyComponent from "./MyComponent.vue";
import {ref, provide} from "vue";
const manager = new OverlayManager();
provide("OverlayManager", manager);
const showOverlay = ref(false);
function onClickButton() {
showOverlay.value = !showOverlay.value;
}
</script>
<template>
<button #click="onClickButton">
Toggle Showing
</button>
<div>
Hello World
</div>
<MyComponent v-show="showOverlay" text="Now Works" />
<MyComponent v-if="showOverlay" text="Works" />
</template>
<style>
.absolute {
position: absolute;
}
.top-100 {
top: 100px;
}
.left-0 {
left: 0;
}
</style>
This behaves exactly how I need it, and I don't have to deal with the quirks that Teleport introduces, and it allows me to have a singleton that is in charge of all of my overlays. The other benefit is that I have access to the parent of where BaseOverlay is initially added in the HTML (not where it's moved). Honestly not sure if this is a good practice, but I'm chuffed at how cute this is and how well Vue integrates with it.
I want to pass text to my react component using props. The important thing is I need the new line symbol ("\n") to work.
My example is very simple:
class UnderlinedText extends React.Component {
render() {
const text = 'Hello\n this is a test'; // this works
// const text = this.props.text; this does NOT work
return (
<div className="textContainer">{text}</div>
);
}
}
ReactDOM.render(
<UnderlinedText />,
document.getElementById("root")
);
.textContainer {
white-space: pre-line;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Using the hardcoded text above everything works as it should (even in debug I see that the string spans in multiple lines). But using it via props like so:
class UnderlinedText extends React.Component {
render() {
const text = this.props.text;
return (
<div className="textContainer">{text}</div>
);
}
}
ReactDOM.render(
<UnderlinedText text='Hello\n this is a test' />,
document.getElementById("root")
);
.textContainer {
white-space: pre-line;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
does not create the new lines and the string in debug has all the new line symbols ("\n") in it.
Where is the difference between those two solutions? And how to solve it?
Because you're using a literal string in
<UnderlinedText text='Hello\n this is a test' />
React is treating it as literal text. \n is \ and n, just like it would be in an HTML attribute.
If you want to supply a JavaScript string with JavaScript escape sequences in it, use {} around it to switch to a JavaScript expression context:
<UnderlinedText text={'Hello\n this is a test'} />
Live Example:
class UnderlinedText extends React.Component {
render() {
const text = this.props.text;
return (
<div className="textContainer">{text}</div>
);
}
}
ReactDOM.render(
<UnderlinedText text={'Hello\n this is a test'} />,
document.getElementById("root")
);
.textContainer {
white-space: pre-line;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Fetching dynamic HTML from an API, the HTML is loading fine but the CSS is not working for this new HTML.
Do you I need to reload the CSS.
import React, { Component } from "react";
import Utility from "../common/Utility";
class Template extends Component {
constructor(props) {
super(props);
this.token = localStorage.getItem("token");
this.client_id = localStorage.getItem("client_id");
}
componentDidMount() {
//fetching dynamic html
Utility.ExecuteData("template", this.token, {
client_id: this.client_id
}).then(result => {
var dynamic_html = document.getElementById("dynamic_html");
dynamic_html.innerHTML = result.data[0].template;
});
}
render() {
return (
<React.Fragment>
<div id="dynamic_html" />
</React.Fragment>
);
}
}
export default Template;
It is possible to get this to work but instead of className you'll need to use the usual HTML class attribute. I've used dangerouslySetInnerHTML here, but it's the same result if you set the innerHTML of the element like you did in your question.
function Template({ html }) {
return (
<div dangerouslySetInnerHTML={{__html: html}} />
);
}
const html = '<div class="heading">With style</div>';
ReactDOM.render(
<Template html={html} />,
document.getElementById('container')
);
.heading {
font-size: 2em;
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="container"></div>
I can't comment so I'm posting it in an answer instead.
I don't know how the html is being returned from the API but I assume the css is either inlined or included as a file in the remote HTML.
If the latter is the case, it might be a possibility that the url to the css file is relative, so calling the url from another server would result in a 404.
$( "#left_arrow" ).click(function() {
if ($(".left_block").hasClass("w-0")) {
$(".left_block" ).removeClass("w-0");
}else{
$(".left_block" ).addClass("w-0");
}
});
In the custom.js I am having the script code above feature is not working when importing with below.
import * as script from '../js/custom.js';
In the Html page when I load this file its working but when writing in react component its not working
You should not mix with jquery with React, What you want to achieve can easily be implemented in React like
class App extends React.Component {
state = {
class: 'w-0'
}
handleClick=()=> {
if(this.state.class === 'w-0') {
this.setState({class: ''})
} else{
this.setState({class: 'w-0'})
}
}
render() {
return (
<div>
<div id="left_arrow" className="left_arrow" onClick={this.handleClick}><i className="fa fa-chevron-left" />Arrow</div>
<div className={"left_block " + this.state.class}>Hello World</div>
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="app"></div>
This question already has answers here:
React Js conditionally applying class attributes
(24 answers)
Closed 6 years ago.
My state is {visibilityFilter: "completed"} or {visibilityFilter: "todo"}. Based on this I want to assign classnames to an element. Something like this,
<span {this.state.visibilityFilter=="completed"?className="active":className:""}>Completed</span>
But it's not working. I tried different variations of it,
{<span this.state.visibilityFilter=="completed"?className="active":className:"">Completed</span>}
But none of them are working. I know that it can work if I create a variable outside return statement and assign it in HTML. Like this,
let classCompleted = this.state.visibilityFilter == "completed"? "active":"";
and then,
<span className={`$(classCompleted)`}></span>
But I want to know how to do evaluate class in return statement.
You're close, you just put the className part outside:
<span className={this.state.visibilityFilter=="completed" ? "active" : ""} onClick={this.handleFilter.bind(this,'completed')}>Completed</span>
Off-topic side note:
Using bind in the onClick every time means you'll re-bind every time that element is rendered. You might consider doing it once, in the component's constructor:
class YourComponent extends React.Component {
constructor(...args) {
super(...args);
this.handleFilter = this.handleFilter.bind(this);
// ...
}
handleFilter() {
// ...
}
render() {
return <span className={this.state.visibilityFilter=="completed" ? "active" : ""} onClick={this.handleFilter}>Completed</span>;
}
}
Another option is to make it an arrow function, if you've enabled class properties in your transpiler (they're in the stage-2 preset in Babel as of this writing, January 2017):
class YourComponent extends React.Component {
// ...
handleFilter = event => {
// ...
};
render() {
return <span className={this.state.visibilityFilter=="completed" ? "active" : ""} onClick={this.handleFilter}>Completed</span>;
}
}
Live example of that one:
class YourComponent extends React.Component {
constructor() {
super();
this.state = {
visibilityFilter: ""
};
}
handleFilter = event => {
this.setState({
visibilityFilter: "completed"
});
};
render() {
return <span className={this.state.visibilityFilter == "completed" ? "active" : ""} onClick={this.handleFilter}>Completed</span>;
}
}
ReactDOM.render(
<YourComponent />,
document.getElementById("react")
);
.active {
color: blue;
}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Use classNames, A simple javascript utility for conditionally joining classNames together.
Note: I've added the state and todo classes to demonstrate working with multiple classes. btw - the comments are not valid JSX, so don't use the code as is.
<span className={
state: true, // always
active: this.state.visibilityFilter === "completed", // conditional
todo: this.state.visibilityFilter !== "todo" // conditional
}>
Completed
</span>}
Example (based on T.J. Crowder`s code):
class YourComponent extends React.Component {
constructor() {
super();
this.state = {
visibilityFilter: ""
};
}
handleFilter = event => {
this.setState({
visibilityFilter: "completed"
});
};
render() {
return (
<span className={classNames({
state: true,
active: this.state.visibilityFilter === "completed"
})} onClick={this.handleFilter}>Completed
</span>
);
}
}
ReactDOM.render(
<YourComponent />,
document.getElementById("react")
);
.state {
color: red;
cursor: pointer;
}
.active {
color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.5/index.min.js"></script>
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>