how to add attribute without actually editing the HTML element in react - javascript

I have a below HTML component created in REACT which I cant edit directly. But I need to add one attribute to one of its HTML element. I can't use jquery as well other wise its easy to do with jQuery.
Below is the base HTML for table which I cant edit directly but I can just use this component in my code.
Challenge : I need to add attribute to the SVG element. e.g. -> data-id="1". Can it be done with CSS or any other way.
<TablePresenter>
<div>
<svg>this is a actually a sort button</svg>
<div>Column 1 Name</div>
</div>
<div>
<svg>this is a actually a sort button</svg>
<div>Column 2 Name</div>
</div>
</TablePresenter>
main file which I can edit is as below.
const MyComponent = () => {
some logic here...
Can we do something here may be CSS or any react hack to get underline component HTML change.
return(
<TablePresenter></TablePresenter>
)
}
export default MyComponent;

Not a recommended way of doing, but using a ref you can achieve this, a sample below:
Add a wrapper div to your parent component (your own component which can be edited) like below and add a ref to it, that will helps you to get the DOM reference of the html inside the <TablePresenter>
const divRef= useRef<HTMLDivElement>(null);
return <div ref={divRef}>
<TablePresenter></TablePresenter>
</div>
// This is an example of getting the reference of the svg.
divRef.current.firstElementChild.firstElementChild.firstElementChild
May be something like this:
const MyComponent = () => {
const divRef = useRef<HTMLDivElement>(null);
useEffect(()=>{
divRef.current.firstElementChild.firstElementChild.firstElementChild.attributes["data-id"]=1
}, [])
return <div ref={divRef}>
<TablePresenter></TablePresenter>
<SuperChild />
</div>
}

Related

How to create an Style Object to bind with react component?

Here I created a sample example. As you can see I add applyColor Style object to apply styles.
import React from 'react'
const applyColor = {
color: 'red'
}
export const App = () => {
const renderData = () => {
return (
<>
<section style={applyColor}>
<p>Paragraph</p>
<div>
<h2>Heading</h2>
<span>Span</span>
</div>
</section>
</>
)
}
return <>
{renderData()}
</>
}
How can I modify applyColor Object to access the child from parent element <section />?
And I dont want to use any third party like styled-components and material-ui/styles.
Thanks:)
In short, no way unless you do it directly to the child.
<div style={{ color: red }} />
That's what the style is made for. However, everything is coded as Javascript code, so you can do it manually via props.
return <Child color="red" />
const Child = ({ color }) => {
return <div style={{ color }} />
}
Every big UI system has their unique way of handling this, ex. Material and etc.
But trusted me, unless you are making small css changes, styled-components like Css-in-Js approach isn't avoidable. Because passing the flag from Parent to Child is just too much work to manage by your own, not difficult but tedious. This is just my two cents.
Or you can use a traditional CSS approach, just put everything through CSS class. This way you can just include one copy of css. But the problem of that approach is that you can't pass a flag into the CSS so in the end you come back to the same boat above :)

Vue Showdown Default Classes

I want to achieve the following say i the MD as
md:'#H1'
I want to render it as
<h1>H1</h1>
I was able to achieve this using VueShowdown
but I want add default class to every h1 tag like
<h1 class="custom">H1</h1>
I got something similar to this here.
But I don't know how to use this in Vue.
Is it even possible in VueShowdown?
Is there any better library which has this functionality?
You can create a simple directive:
Vue.directive('default-classes', (parentElement) {
const els = parentElement.querySelectorAll('h1')
els.forEach((el) => {
el.classList.add('custom')
})
})
Then apply that directive to the VueShowdown component:
<VueShowdown v-default-classes :markdown="markdownBinding" />

Using the hidden attribute vs conditionally rendering a component

I have recently discovered an alternative to conditionally rendering a component in JSX, which is to use the hidden HTML attribute.
Example
function Parent() {
return {!hideChild && <Child />}
}
vs
function Parent() {
return <Child hidden={hideChild} />
}
function Child({ hidden }) {
return (
<div hidden={hidden} >
//my content
</div>
)
}
So far I have not noticed any performance or alike issues when using hidden. In saying that, are there any downsides to have lots of HTML on the page that is hidden?
For me, this approach has served well when I want to retain the component state and have the functionality of toggling the visibility of the components UI.
Is this bad practice? Should we be conditionally rendering components instead?
The difference is that when using conditional rendering, the logic inside the conditionally rendered UI will not be executed if the condition fails.
But using the hidden attribute will execute the logic but only hides the UI.
Example:
import React from 'react';
const A = () => {
console.log('A rendrerd');
return <h1>A</h1>;
};
const B = ({ hidden }) => {
console.log('B rendrerd');
return <h1 hidden={hidden}>B</h1>;
};
const Test = () => {
return (
<div>
{false && <A />}
<B hidden={true} />
</div>
);
};
export default Test;
A will never call its console.log statement.
B is hidden but it will log B rendered.
I think this is worth mentioning. hidden attribute acts more or less like display: none with css. The truth is "the component will be rendered but only hidden from your display."
Just to illustrate, below is a photo of some html and their output. you realize that the output doesn't show the <p> that is decorated with hidden attribute but if when you inspect the rendered code, you realize that <p> was actually renderd. So you can image have multiple components in react where they will all be rendered but only displayed based on that hidden attribute.
Well I may not be sure of the performance involved but it's obvious that doign a simple if() to condition render a component will depending on the size of your components be much quicker than rendering everything and only relying on their hidden attribute decoration. And this also means that a user can just Inspect element and remove hidden attribute to display that component which is intended to be hidden.

Next.js custom javascript not running on it's own page

I want to add a custom javascript in my next.js page
but my script seems doesn't load properly.
this is not my full code,
but i hope it can represent my problem.
export default class extends React.Component {
render() {
return (
<div>
<div id="myDiv">
<p>Inside My Div</p>
</div>
<script dangerouslySetInnerHTML={{
__html: '$(function() {
document.getElementById("myDiv").style.display = "block";
})'
}} />
</div>
)
}
}
why i can't change style.display like code above ?
As you are using react it's better to use the api that react provided. There is a method called Ref.
For that it's better to convert the class based component to functional component. Then import useRef from react
Declare the ref
const myRef = useRef()
Now assign ref to your div
<div ref={myDivRef} id="myDiv">
Now in your function access it like bellow
myDivRef.current.style.display = 'block'
First of all, react will insert this with innerHTML which means it will inserted as DOM text. Second of all, if you're using Next.js, you should have in mind that on server side, you don't have access to the document object and you should check if it is not undefined.
As a solution, why don't you try putting it in componentDidMount as here you have the document object and you can change the style.
componentDidMount() {
document.getElementById("myDiv").style.display = "block";
}

React replace event.target with input

UPDATE
Here's are some demos
contentEditable demo - requires double click for H1 to become editable
replace with input demo - adopts event.target styles but makes the UI 'twitch' when rendered
So I have some functional components, let's say:
component1.js
import React from 'react';
const component1 = props => (
<div>
<h1>Title</h1>
</div>
);
export { component1 };
They are variable. event.target could be anything with text, so paragraph, heading, anything. I'm trying to let users edit content inline by clicking on it, so I'll pass a function editMode to these functional components, that'll update parent state with editing info, let's say like this:
<h1 onClick={event => {editMode(event, props.name, props.title, 'title')}}>title</h1>
This changes parent local state to have all the necessary information to grab the value from redux, define a target etc. For this example, props.name is the name of the component, props.title is the value, and 'title' is object key in redux.
So I'll add something to my component1.js and make it look a bit like this:
import React from 'react';
const component1 = props => (
<div>
{props.editState === 'true' &&
<EditLayout
name={props.name}
target={props.target}
value={props.value}
onChange={event => someFunc(event)}
/>
}
<h1>Title</h1>
</div>
);
export { component1 };
Now this works fine, except it doesn't scale. EditLayout, in this case, will just return an input with correct value. What I need it to do is to adapt to whatever is being clicked, get font size, background, padding, margin, position. Am I doing this right? Every way I try, I run into huge issues:
Idea 1 - move EditLayout component outside of the functional component
Issue: positioning
So I'll move EditLayout to parent component that contains both component1.js and EditLayout. This will allow me to manipulate it from inside the functional component, without having to include it everywhere. I'll then grab coordinates and other important information from event.target like so:
const coords = event.target.getBoundingClientRect();
const offsetX = coords.left;
const offsetY = coords.top;
const childHeight = coords.height;
const childWidth = coords.width;
const childClass = event.target.className;
I'll then wrap the EditLayout to return a container which contains an input, and apply size/coordinates to the absolutely positioned container. This'll present an issue of input being offset by a random amount of pixels, depending on how big/where is the event.target.
Idea 2 - pass relevant computed styles to EditLayout
Issue: twitching on render, and I have to add EditLayout for every possible event.target there is, as well as condition its' render
So I'll grab all important computed styles like this:
const computedTarget = window.getComputedStyle(event.target);
const childMargins = computedTarget.marginBottom;
const childPaddings = computedTarget.padding;
const childFontSize = computedTarget.fontSize;
const childTextAlign = computedTarget.textAlign;
And pass it to component1.js, and then pass it to EditLayout component inside the component1.js. I'll then condition theevent.target to hide if it's being edited like this:
<h1 className={ props.target === 'title' ? 'd-none' : ''}>Title</h1>
And condition the EditLayout to show only if it's needed:
{props.target === 'title' && <EditLayout />}
In this example, clicking h1 will show the input, but the layout itself with twitch on render. Input will have the exact same margin and font size as the h1, or event.target, but it'll appear bigger and extend the layout. Demo:
Idea 3 - Use conditional contentEditable
Issue: Requires double click to enable, doesn't work in safari, doesn't let me preselect the value
This is the weirdest of them all. I figured it'd be pretty simple, do something like this inside the functional component render:
<h1 contentEditable={props.target === 'title'} onClick={event => props.setTarget(event)}>Title</h1>
However, I have to double click to enable it. I have no idea why, if I attach a console log every time onClick is fired, I'll get correct outputs, I'll get the correct target value as well. I've tried numerous ways, but it simply requires double click. Even attempted to handle this inside the functional component, as most of the stuff is handled by a parent component, doesn't make a difference.
I have oversimplified the examples, so it's safe to assume/understand the following:
I am passing props in a correct fashion, they aren't undefined
I am using bootstrap
I am using styled components, and EditLayout is a styled component
which accepts props and turns them into CSS like: font-size: ${props
=> props.fontSize};
The values should be correct, I am not manipulating anything I get back from getComputedStyle() or getBoundingClientRect()
I am keen on keeping my functional components functional, and easy to
add. Functional components, in this case, are simple HTML structures,
and I'd like to keep them as simple as possible
So there's a neat solution to contentEditable requiring two clicks instead of one, instead of binding onClick and passing it to enable contentEditable, simply keep contentEditable true and handle the change however you like. Here's a working h1 that doesn't require two clicks to enable contentEditable, unlike the one in the demo
<h1
className="display-4 text-center"
contentEditable
suppressContentEditableWarning
onBlur={event => updateValues(event)}
>
Title
</h1>
The available methods for trigger update could be onBlur or onInput.

Categories