I have an app that simply hides content Hidden.svelte:
<script>
let shown = false;
function show() {
shown = true;
}
</script>
<svelte:options accessors={true}/>
{#if shown}
<slot/>
{/if}
Parent App.svelte:
<script>
import Hidden from 'Hidden';
let child;
</script>
<Hidden bind:this={child}>
Content
</Hidden>
<button on:click={() => child.shown = true}>Show</button>
So, child's shown can be easily set due to <svelte:options accessors={true}/> in parent
But, I want to use method show() since it can not only set shown value, but also perform some magic
Thx to Chrome's DevTools, I found that all components have an Array with props and methods, that could be accessed via some .$$.ctx, so Hidden's show() method can be called like this:
<button on:click={() => child.$$.ctx[2]()}>Show</button>
But) You know) Is there are legal way to do it?
Hidden.svelte
<script>
let shown = false;
export function show() {
shown = true;
}
</script>
{#if shown}
<slot/>
{/if}
App.svelte
<script>
import Hidden from './Hidden.svelte';
let child;
</script>
<Hidden bind:this={child}>
Content
</Hidden>
<button on:click={() => child.show()}>Show</button>
The call to child.show() can actually be simplified, but I thought this could make it harder to figure out what's going on in the example. You can do just:
<button on:click={child.show}>Show</button>
Related
hope you having great days,
i'm trying to get div for change its attribute value
but document.getElementById() is not working
i put the statement for after the div is completly load,
and even tried windows.load but nothing workout,
any suggestions?
var size_list = [[76.01, 77.81,23.99,11.09,0,11.09]
,[69.9, 71.56,20.51,14.22,9.59,14.22]
,[64.1,65.63,17.56,17.19,18.34,17.19]
,[59.22,60.63,15.15,19.69,25.64,19.69]
,[54.79,56.09,12.87,21.95,32.34,21.95]]
function size_update(src,index) {
console.log("i'm working");
let element = window.getComputedStyle(document.getElementById("thisid"),null)
element.setProperty('.height',size_list[index][0]+'%');
element.setProperty('width',size_list[index][1]+'%');
element.setProperty('top',size_list[index][2]+'%');
element.setProperty('right',size_list[index][3]+'%');
element.setProperty('bottom',size_list[index][4]+'%');
element.setProperty('left',size_list[index][5]+'%');
}
const Videoframe = ({src,index,title,before_span,span,after_span,subtitle}) =>{
try{
// do something
return(
<div>
<script>
function (){console.log("start!")};
</script>
<div className="videoframe" name = "thisid" id = "thisid" >
<div className="textgroup1">
<div className="title">{title}</div>
<div className="main">
<span>{before_span}</span>
<i className="better">{span}</i>
<span> {after_span}</span>
</div>
<div className="padding" />
<div className="subtitle">
{subtitle}
</div>
</div>
</div>
<script>
function (){console.log("ended!")};
</script>
</div>
);}
finally{
size_update(src,index);
}
}
export default Videoframe;
export { size_list }
tried googling and window.load
window.getComputedStyle() returns the style of the object, but you need the DOM-Element.
To get an element once the document is loaded you need to use the window load event by doing either
window.addEventListener("load", () => {
let element = document.getElementById("thisID");
});
or
window.onload = () => {
let element = document.getElementById("thisID");
};
In both cases, you supply a function to be executed once the document is loaded. Note that the code inside the eventhandler is executed only when the site is loaded, so after the code which is written after this code snippet. If you want to access the element, you'll need to write the logic for the element inside the function.
JavaScript:
const dispatchEvent = createEventDispatcher();
let dialog: Dialog;
HTML
<Dialog bind:this={dialog} on:close={() => {dispatchEvent("deletedPost", postIndex)}} modal={true} open={false}>
... Here I want to insert some HTML to be used in the slot of the "Dialog" component.
</Dialog>
My question is, how do I set the on:close and the inner HTML dynamically via JS?
I have this demand because this Dialog component should be reused for multiple purposes by its parent component. Sometimes it displays info about a post has been created, sometimes deleted, sometimes edited and so on.
And each of the cases requires different content in the component as well as a different handler for the close event emitted by the Dialog.
Should I use something like
dialog.$on = handlerFunction;
// I didn't find a possible property for setting HTML though
Thanks!
There currently is no way to programmatically interact with the slots (unless you want to investigate the Svelte internals and rely on implementation details which may change).
Either pass in a full component via the props (which can be rendered using <svelte:component>), insert only text or use {#html} to render HTML content (which is not recommended for security reasons).
(The syntax for the events is dialog.$on('close', event => ...) as detailed in the client-side API section.)
Example for all mentioned property types:
<!-- Dialog.svelte -->
<script>
import { createEventDispatcher } from 'svelte';
export let open = true;
export let component = null;
export let text = null;
export let html = null;
const dispatch = createEventDispatcher();
$: dispatch(open ? 'open' : 'close');
</script>
<div>
<h2>Dialog</h2>
{#if component}
<svelte:component this={component} />
{/if}
{#if text}
{text}
{/if}
{#if html}
{#html html}
{/if}
<div><button on:click={() => open = false}>Close</button></div>
</div>
Usage:
// ...
import Content from './Content.svelte';
const d = new Dialog({
// ...
props: { text: 'Hello world' },
});
// or
const d = new Dialog({
// ...
props: { html: '<input placeholder="Text here"/>' },
});
// or
const d = new Dialog({
// ...
props: { component: Content },
});
d.$on('close', () => d.$destroy());
REPL
As noted, #html is dangerous and ideally should only be used with XSS sanitization in place.
Is it possible to execute a <Script/> every time the props of a react/nextjs component change?
I am converting markdown files to html using marked and, before rendering the html, I would like to have a [copy] button on each <pre> block (those are the code blocks). I have a <script/> that iterates through the <pre> blocks of the DOM document.querySelectorAll("pre") and injects the button needed. If the html changes though at a later stage, then I have found no way to re-run the script to add the copy buttons again.
I have the impression that this is not a very react/nextjs way of doing this, so any hints would be appreciated.
The Script to add the copy buttons. I have added this as the last tag of my <body>:
<Script id="copy-button">
{`
let blocks = document.querySelectorAll("pre");
blocks.forEach((block) => {
if (navigator.clipboard) {
let button = document.createElement("img");
button.src = "/images/ic_copy.svg"
button.title = "Copy"
button.id = "copy"
button.addEventListener("click", copyCode);
block.appendChild(button);
}
});
async function copyCode(event) {
const button = event.srcElement;
const pre = button.parentElement;
let code = pre.querySelector("code");
let text = code.innerText;
await navigator.clipboard.writeText(text);
button.src = "/images/ic_done.svg"
setTimeout(()=> {
button.src = "/images/ic_copy.svg"
},1000)
}
`}
</Script>
the React component. Not much to say here. The content is coming from the backend. Not sure what would be the 'React' way to do this without the script.
export default function Contents({ content }) {
return (
<div className='pl-2 pr-2 m-auto w-full lg:w-2/3 mb-40 overflow-auto break-words'>
<div className="contents" dangerouslySetInnerHTML={{ __html: content }} />
</div>
)
}
You should absolutely not do this and instead incorporate this logic into your react app, but if you must you can leverage custom window events to make logic from your html script tags happen from react.
Here is an example script:
<script>
function addEvent() {
function runLogic() {
console.log("Stuff done from react");
}
window.addEventListener("runscript", runLogic);
}
addEvent();
</script>
And calling it form react like this:
export default function App() {
const handleClick = () => {
window.dispatchEvent(new Event("runscript"));
};
return (
<div className="App" onClick={handleClick}>
<h1>Hello</h1>
</div>
);
}
For instance, I want to behave a Button as a normal button and enhance the component.
<Button type="submit" className="btn">Click me!</Button>
leads to an html element:
<button type="submit" className="btn">Click me!</button>
Is there a way to write the Button component like this?:
const Button = ({children, ...htmlAttributesOnly}) => (
<button {...htmlAttributesOnly}>{children}</button>
)
The idea behind is to make a component as flexible as possible by giving access to all of its html elements' attributes. Or do I have to repeat every html element attribute?
You were really close to an answer, just wrap your props in curly braces:
const Button = ({ children, ...rest }) => (
<button {...rest}>{children}</button>
)
You can create a Button component like this.
export default function Button(props) {
return <button {...props}>{props.children}</button>;
}
Later you can use it like this.
<Button onClick={()=>alert("hello")} style={{padding:10,border:'none',backgroundColor:'white'}} >Click Me</Button>
So I have two .js files (are they also called modules?). The first .js file is a class-based component. It has handleClick() as well as render(). It looks like this (I've actually removed a lot of the code to make it appear shorter here):
handleClick(event) {
event.preventDefault()
console.log('handleclick')
this.initializeFetchApiAndSetState()
}
//Helper Function
checkGuessForCorrectAnswer() {
console.log('correct answer!')
}
render() {
return (
<div className="container-main">
<MultipleChoices
onClick={this.handleClick}
data={this.state.guess1}
/>
<button
className='Submit'
onClick={this.handleClick}
>
Submit
</button>
</div>
)
}
The button above works fine in that I can click on it and it'll console log the word 'correct answer!'. But for some reason, when I try to pass onClick to the "MultipleChoices" file/module it doesn't console log 'correct answer!'. The MultipleChoices.js file looks like this:
import React from "react"
function MultipleChoices(props) {
return(
<div>
<div className="button-grid">
<button
className="btn"
value={props.data}
onClick={props.handleClick}
>
{props.data}
</button>
</div>
</div>
)
}
export default MultipleChoices
Why can the button activate onClick in the first file, but not when I try to pass onClick to the MultipleChoice.js (which also has a button)?
In your upper component, you need to replace the onClick property with a handleClick property.
<MultipleChoices
handleClick={this.handleClick}
data={this.state.guess1}
/>
Because inside the Multiple Choices component you are calling the handleClick method from the properties (which is not set)
In your parent component you have given name to your property as onClick, while you are trying to acces it in children component as prop.handleClick.