How to use window.postmessage to communicate with parent window - javascript

I'm creating a react app that I want my users to embed on their websites. This app will be shown in two iframes but I want to only show one iframe at a time. So one part of the app would be a simple button, the second would be a form that pops up once a user has decided to use the app (it's a chat app.)
I have set up a very simple component that displays the button so that it can send a message to the parent website. I'm not sure what I'm doing wrong but when the window.postmessage event is sent, I get the error:
Uncaught DOMException: Blocked a frame with origin "http://localhost:3002" from accessing a cross-origin frame.
Here is the example parent website code:
<h1>Hello! This is some static content</h1>
<iframe src="http://localhost:3002"
style="width: 108px; height: 50px; padding: 0px; margin: 10px 20px; position: fixed; bottom: 0px; right: 0px; overflow: visible; opacity: 1; border: 0px; z-index: 999998; transition-duration: 250ms; transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1); transition-property: opacity, top, bottom;"
id="dbhchat"></iframe>
<h3>Thanks for checking out my blog!</h3>
<script>
window.addEventListener('message', event => {
// IMPORTANT: check the origin of the data!
if (event.origin.startsWith('http://localhost:3002')) {
// The data was sent from your site.
// Data sent with postMessage is stored in event.data:
console.log(event.data);
} else {
// The data was NOT sent from your site!
// Be careful! Do not use it. This else branch is
// here just for clarity, you usually shouldn't need it.
return;
}
});
</script>
and my component that sends the message to the parent window:
import React from 'react';
import { IconButton } from '#material-ui/core';
import PhotoCamera from '#material-ui/icons/PhotoCamera';
import './App.css';
function App() {
const send = () => {
if (window && window.parent) {
console.log('we have message sending here', window.parent);
window.parent.postMessage('message', 'http://localhost:3002', false);
}
}
return (
<div className="App">
<header style={{ background: 'red' }} className="App-header">
<IconButton onClick={send} color="primary" aria-label="upload picture" component="span">
<PhotoCamera />
</IconButton>
</header>
</div>
);
}
export default App;
Thanks in advance for any help figuring this out!

I found a bit of a quirky solution to the problem. I don't really know why this works but simply adding e.preventDefault() to the react send function worked fine after that.
const send = (e) => {
e.preventDefault();
if (window && window.parent) {
window.parent.postMessage({message: 'The message is being set up here', hide: 'dbhchat', show: 'dbhchat'}, '*');
}
}
after adding that, all worked fine. If anyone can provide more details as to why that'd be great!

Related

Using Radix UI props does not work in React JS application

In my react Js application i am using Radix. I need to customizze toast component changing the position where the component should appear.
For this, the API provides the next way:
<ToastProvider swipeDirection="up">
So swipeDirection is responsible for this.
ISSUE: Using the above changes, the component stil appear at the bottom even with changed prop.
Question: How to make the component to appear at the top and why the prop does not work? demo: https://codesandbox.io/s/z688kv?module=App.js&file=/App.js:3909-3944
Sadly there is no "position" prop out of the box for this component. The swipeDirection="up" prop only specify that you can delete a toast by swiping it to the top. You can still specify where to place the toasts with pure css. Just update the styles of the ToastViewport component.
Default styles are:
position: fixed;
bottom: 0px;
right: 0px;
So to have the toasts in the top right corner for example just use:
<ToastViewport style={{ top: "0px" }} />
Adding on #johannchopin answer, if you want to position the toast to the left-bottom for example.
First, some changes to the slideIn animation:
const slideIn = keyframes({
from: { transform: `translateX(calc(-100% + ${VIEWPORT_PADDING}px))` },
to: { transform: "translateX(0)" }
});
const swipeOut = keyframes({
from: { transform: "translateX(var(--radix-toast-swipe-move-y))" },
to: { transform: `translateX(calc(-100% + ${VIEWPORT_PADDING}px))` }
});
notice the use of -100% instead of 100%
and a small change to the StyledViewport default position:
const StyledViewport = styled(ToastPrimitive.Viewport, {
position: "fixed",
bottom: 0,
left: 0,
//other styles removed to keep the code short
});

sveltekit adds new page on top of old one

I just got a really unexpected bug in my sveltekit application and I can't find anything online talking about it
I have a normal sveltekit application but instead of hydrating the new code when navigating to a new page, it just adds the new code on top of the old one, when i refresh the page it removes the old code (from the previous page)
Edit: after a little bit more exploring I realized it only happens on one page, what part of my code could make this happen?
I had the same issue when having a page loading indicator on SvelteKit for internal navigating. Any page DOM modification, to display the loading indicator during the page navigating, caused the paged appearing twice error. The workaround was to not modify the page during the navigating and use only CSS to display, animate and hide the loading indicator.
Here is my loading indicator code and Here is my documentation regarding the issue. I am not sure if this an internal bug in SvelteKit, so I did not file any bug reports, as I do not have a clean repeatable example. You can also see the fixed page loading indicator on the action on this page if you click any of the blockchains.
<script>
/**
* Svelte does not give a load indication if you hit a link that leads to a page with slow load() function.
* Svelte uses internal router, not server-side loading.
* Thus, we need to manually give some indication in the user interface if the loading takes more than a blink of an eye.
*
* The component is originally made for https://tradingstrategy.ai
*
* Based on the original implementation https://github.com/shajidhasan/sveltekit-page-progress-demo by Shajid Hasan.
*
* As this component is absolutely position, you can put it at any part of your __layout.svelte.
*/
import { onDestroy, onMount } from 'svelte';
import { writable } from 'svelte/store';
import { tweened } from 'svelte/motion';
import { cubicOut } from 'svelte/easing';
const navigationState = writable();
const progress = tweened(0, {
duration: 3500,
easing: cubicOut
});
const unsubscribe = navigationState.subscribe((state) => {
// You will always get state=undefined
// event on the server-side rendering, so
// safely ignore it
//console.log("The loading state is", state);
if (state === 'loading-with-progress-bar') {
progress.set(0, { duration: 0 });
progress.set(0.8, { duration: 5000 });
} else if (state === 'loaded') {
progress.set(1, { duration: 1000 });
}
});
onMount(() => {
// progress.set(0.7);
});
onDestroy(() => {
unsubscribe();
});
</script>
<!-- See the (little) documentation of special SvelteKit events here https://kit.svelte.dev/docs#events -->
<svelte:window
on:sveltekit:navigation-start={() => {
// If the page loads fast enough in the preloading state,
// never display the progress bar
$navigationState = 'preloading';
// Delay the progress bar to become visible an eyeblink... only show if the page load takes too long
setTimeout(function() {
// After 250ms switch preloading to loading-with-progress-bar
if($navigationState === 'preloading') {
$navigationState = 'loading-with-progress-bar';
}
}, 500);
}}
on:sveltekit:navigation-end={() => {
$navigationState = 'loaded';
}}
/>
<!--
Make sure the container component is always in the DOM structure.
If we make changes to the page structure during the navigation, we get a page double render error:
https://stackoverflow.com/questions/70051025/sveltekit-adds-new-page-on-top-of-old-one
Not sure if this is a bug or a feature.
Thus, make sure any progress animation is done using CSS only.
-->
<div class="page-progress-bar" class:loaded={$navigationState === 'loaded'} class:preloading={$navigationState === 'preloading'} class:loading={$navigationState === 'loading-with-progress-bar'}>
<div class="progress-sliver" style={`--width: ${$progress * 100}%`} />
</div>
<style>
/* Always stay fixed at the top, but stay transparent if no activity is going on */
.page-progress-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 0.5rem;
background: transparent;
z-index: 100;
opacity: 0;
transition: opacity 0.5s;
}
/* After transitioning from preloading to loading state, make the progress bar visible with CSS transition on opacity */
.page-progress-bar.loading {
opacity: 1;
transition: opacity 0.5s;
}
.progress-sliver {
width: var(--width);
background-color: var(--price-up-green);
height: 100%;
}
</style>
I also saw this issue with the transition directive. The new page loads while the exit animation is playing. I used local transitions to solve this.
https://svelte.dev/tutorial/local-transitions

Possible to trigger on Vimeo video click?

Is it possible to trigger an action on click if a video is set to background=1 (no controls)?
This is a Vimeo video, plus account (where background=1 is permitted).
Essentially, I have a Vimeo video with no controls set to loop and autoplay with a volume of 0. My implementation has an icon overlayed on top of the video, in the center. When clicked, it is set to full volume and the icon is hidden.
Once the volume is set to 1 and the icon is hidden, the person viewing should have the option of clicking the video so as to mute it (set volume to 0).
The problem is that I am not able to figure out how to target this click. I have tried attaching an .on('click') to the iframe, its parent, and as far up the chain as I can go but beyond that first click of the icon, the click is never registered.
Can anyone please offer any pointers on how to target a click on a Vimeo iframe video (or its parent container, etc)?
Here is my code thus far:
var iframe = document.getElementById('vimeo-video');
var player = new Vimeo.Player(iframe);
player.ready().then(function() {
var volume = 0
player.setVolume(volume);
$('#vimeo-video-play').on('click', function(event) {
if (volume > 0) {
player.setVolume(0);
} else {
player.setVolume(1);
}
$('#vimeo-video-play').hide();
});
});
Vimeo Promises
It appears that if you use Vimeo API, you must return a Promise. If you just want to do a simple task such as controlling the volume, the documentation gives this example:
player.setVolume(0.5).then(function(volume) {
// volume was set
}).catch(function(error) {
switch (error.name) {
case 'RangeError':
// the volume was less than 0 or greater than 1
break;
default:
// some other error occurred
break;
}
});
Not simple, and it's overkill. It's not apparent by looking at it but if you see then(), await async, new Promise you can say with 99.9% certainty that a Promise will be returned. I haven't looked deep enough into player.js but I think each method is wrapped in a Promise so as far as I could tell, we can just return the method without using all of that extra crap. So compare the above code to the following:
var sVol = player.setVolume(1); return sVol;
So I believe when invoking a Vimeo API method, we can return the function as a value. There's no work involving what exactly that value is because it's going to be either resolved or rejected. A Promise is also immutable so returning the function itself should be a guaranteed resolve (concerning Vimeo methods, not Promises in general).
Overlay Layout
Instead of clicking an iframe which is filled with a video player that will do 100 other tasks except your custom callback, you need to click an element outside of the iframe. As a background video without controls, you are very limited. I suggest an element that covers the iframe player edge to edge so that the user clicks it and nothing else. The following are the steps to setup an overlay:
Wrap the iframe player (#vFrame0 in the demo) in a relpos🞳 parent container (a.k.a. .box)
Place an older sibling🗡 abspos🞳 element (a.k.a. .overlay) inside the parent with the iframe player.
Set the older sibling "above" the player by setting its z-index at l more than the iframe player and the necessary CSS properties (see demo CSS for .overlay) to ensure that the older sibling covers the iframe player edge to edge completely.
Register the click event to the overlay element so when the player ignores the click event, the event will bubble back to the older sibling overlay element. The overlay element is now a proxy of sorts and will run the callback:
var sVol = player.setVolume(1); return sVol
Demo
This Demo does not function because there are conflicts between Vimeo's connections and SO's security measures. For a functioning Demo review this Plunker or this Plunker.
<!DOCTYPE html>
<html>
<head>
<base href="https://player.vimeo.com/api/demo">
<meta charset='utf-8'>
<style>
.box {
display: table;
border: 3px dashed red;
position: relative;
}
.overlay {
cursor: pointer;
display: table-cell;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
min-height: 100%;
z-index: 1;
}
.overlay::before {
position: absolute;
cursor: pointer;
display: block;
content: '🕪';
font-size: 2em;
width: 2em;
height: 2em;
color: cyan;
opacity: 0;
transition: opacity .5s ease 3s;
}
.overlay:hover::before {
opacity: 1;
transition: .5s ease;
}
.mute.overlay::before {
content: '🕩';
}
</style>
</head>
<body>
<figure class='box'>
<figcaption class='overlay mute'></figcaption>
<div id='vFrame0' data-vimeo-id="76979871" data-vimeo-autoplay data-vimeo-background></div>
</figure>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src='https://player.vimeo.com/api/player.js'></script>
<script>
var player = new Vimeo.Player('vFrame0', options);
var options = {
mute: true
};
$('.overlay').on('click', function(e) {
var state = $(this).hasClass('mute') ? player.setVolume(1) : player.setVolume(0);
$(this).toggleClass('mute');
return state;
});
</script>
</body>
</html>
🞳relpos: position: relative | abspos position: absolute
🗡older sibling an element that is positioned before the element being referred to.
$('.button-trigger').click(function (e) {
let element = $('iframe').attr('src');
element = element.replace("autoplay=0", "autoplay=1");
$('iframe').attr('src', element);
});

ERROR Error: Uncaught (in promise): invalid link: SosPopPage

I am coding a SOS page, when I click the button that I want it showing a popup page like this. Then user can choose phone number.
sos.html page
<button ion-button color="light" (click)="openSosPop()">Call</button>
sos.ts page
openSosPop() {
this.openModal('SosPopPage');
// let contactModal = this.modalCtrl.create(SosPopPage);
// contactModal.present();
}
openModal(pageName) {
this.modalCtrl.create(pageName, null, { cssClass: 'inset-modal' })
.present();
}
sos.css page
ion-modal.inset-modal {
// transparent black background overlay
background-color: rgba(0, 0, 0, .5) !important;
transition: opacity .25s ease-in-out;
padding: 20vh 10vw;
}
I am not using lazy loading
If you are not using lazy loading, it is not an IonicPage.
Adding IonicPage decorator allows you to use string names to handle pages.
This will automatically create a link to the MyPage component using the same name as the class, name: 'MyPage'. The page can now be navigated to by using this name.
Since you are not lazy loading, you cannot use a string to navigate or create modals and popups. You have to use the actual page/component.
Import the page.
import <path_to_page>;
this.openModal(SosPopPage);//create the modal.

Slow down, console. (javascript/jquery)

I'm working on a console game and I don't like the speed at which the console.logs and how little time there is in between prompts and such. Is there a javascript/jquery method to slow down the game? To that effect, can I simply delay() every line (which sounds tedious), or if I were to use setTimeout(), would I theoretically have to split my game into a lot of different functions and set timeouts or intervals? What are your suggestions?
snippet for example:
alert('~~ WELCOME TO [x] ~~');
console.log('Before we get started, let\'s find out a little about you.');
var usr = {
name : prompt('What\'s your name?'),
age : prompt('How old are you?'),
clr : prompt('What\'s your favorite color?'),
pref : prompt('Which [x] is your favorite?'),
}
console.log('The air smells pungent today, and the weather is perfect.');
console.log(''); console.log('');
console.log('Did you hear that? I think something may be following us down the path to the [x]...');
console.log('');
var alpha = prompt('');
There will be if/elses, switches, all sorts of functions and choices. But I want the feel of a text-based console game.
I plan on adding a lot of different routes, features, and hopefully movement at some point. But that's all beside the point. If anyone knows a way or two to slow down a game that would follow this guideline, post any suggestion.
Most users consider prompts to be annoying and ugly. A user won't be able to interact with anything else including other tabs or console during the execution of prompts. Moreover, it is very inconvenient to work with it as a developer, because they are not configurable and they are hard in development, support and, especially, debugging.
It is a better idea to implement an HTML page which will interace with a user. So, you will be able to customize it and look nice.
For example, you can create a page which looks like chat - text window and input at the bottom. Like this:
function tell(text)
{
$("<p/>").text(text).appendTo($('#chat'));
}
function ask(text, callback)
{
$("<p/>").text(text).addClass('question').appendTo($('#chat'));
$('#send')
.prop('disabled', false)
.one('click', function() {
var text = $("#message").val();
callback(text);
$("#message").val("");
$(this).prop('disabled', true);
});
}
tell("Hi");
ask("What is your name?", function(x) {
tell("So strange...my name is " + x + ", as well...");
});
#chat {
padding: 20px;
position: absolute;
top: 0px;
bottom: 20px;
left: 0px;
right: 0px;
background-color: #DDDDDD;
}
#message {
position: absolute;
bottom: 0px;
left: 0px;
height: 14px;
width: 80%;
}
#send {
position: absolute;
bottom: 0px;
left: 80%;
width: 20%;
height: 20px;
}
.question {
font-weight: bold;
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="chat"></div>
<input type="text" id="message"/>
<input type="submit" id="send" disabled/>
It is just an example. You may add any things like delays or CSS styling.
Create your own console, alert and prompt methods wrapping the natives.
For instance:
function logConsole(text, delay) {
window.setTimeout(function() {
console.log(text);
}, delay || 0);
};
You can change the 0 value in above to be a default delay if no delay argument is passed in.
logConsole('The air smells pungent today, and the weather is perfect.', 1000);
Just an idea

Categories