I'm trying to open Spotify's login page in a new popup window so that user can give my application permission to access their spotify data. I'm making a server side request to the Spotify and then sending the receiving HTML from the server to the client as per the auth flow.
The issue arises when I try to open the popup and I see nothing but three loading dots. The code I received from spotify is working as I have tested it in isolation and my popup code is also working but I can't get both of them to work together.
HTML
<html>
<head>
<title> Dashboard </title>
</head>
<body>
<button id='myBtn'>Click</button>
</body>
</html>
JS
const btn = document.getElementById('myBtn');
btn.addEventListener('click', function(){
const popupWindow = window.open('', 'popup', "width=600,height=450,scrollbars=no");
const codeData = `<!DOCTYPE html>
<html id="app" lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Login - Spotify</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<base href="/">
<link rel="icon" href="https://accounts.scdn.co/sso/images/favicon.ace4d8543bbb017893402a1e9d1ac1fa.ico">
<script defer src="https://accounts.scdn.co/sso/js/indexReact.80b656d1587f8f6dc120.js" sp-bootstrap></script>
<meta id="bootstrap-data" sp-bootstrap-data='{"phoneFeatureEnabled":true,"previewEnabled":false,"user":false,"tpaState":"AQAUyYkSh59ZWwsaoxktt0QP0peVoqudQySwwaRggIRzjIDvJXSPb1XkA+h2SMWxsB4S7lHzObldMs9cX00OyB2SBsLzsgtTmYjqR1Ep6pTiDLfTa8LwlUu7mfJr2i2ZilhhGkRzXztEVTtBeyl1JvTF2OS7QqA/bOYdFtz6yQPiJ7dA5LKaOdJQi6gIqWzfGqIXnTyxxx9Ok/uRa2YSzaPtUPGrjRAx1Z4pkUTc6QmEPJbl+fn+QiJT9LTUrIWw/0rmCj61J0EIY30wX5lFftxu7GWLorgOcG+iGluxKqGMP0dG41j8nKRZzFqV40MlgAoJOk8KYSYZ8Q4Xeeejvug+hVAQylQ1f3o98E+/KC19RPhfrENshoyNm9Xhz+aV+n3qXrTkSE/2wh0JDSVc+uK+vT0sk44eBji9FSnl2C+N+Pmn16uCOBvDaUFm+MLEdTgIwncGT6EmFamF7+JwHkf6j7aeySj6rr7NSkZBWrUivkmRi2Lsfw9B2Z5LGRtXxtAC9k/h2uhDNq9RW/WMxt6PBQEIH5AzbN8P35Ns8thrBSIr/ajViJXQ8q9rckEYMOCfsP3wABSWarVkPd8L2H5EitFYK6e5usTlEW3w3/kALZ9T+1I9LLrGIOt8XQGtvR+F1daROn83bt/oKkDuHvHYx4bX7+NnWHJHKoX2Dahta1q16auFoppSolY/XBVdCGO1J9RePaoOMja7Pj2L0LfKrePy7zse0n+0xiMQ9oZtXQ==","geoLocationCountryCode":"IN","state":"","flowCtx":"dac5b50d-741f-4095-b401-fa87bfd499f4:1705629847","BON":["0","0",-1672731924]}' sp-component="login" sp-translations-data='eyJlcnJvclRpdGxlIjoiRXJyb3IiLCJsb2dpblRpdGxlIjoiTG9naW4iLCJmb3Jnb3RZb3VyUGFzc3dvcmRVc2VybmFtZSI6IkZvcmdvdCB5b3VyIHBhc3N3b3JkPyIsImRvbnRIYXZlQW5BY2NvdW50IjoiRG9uJ3QgaGF2ZSBhbiBhY2NvdW50PyIsImlucHV0VXNlcm5hbWUiOiJFbWFpbCBhZGRyZXNzIG9yIHVzZXJuYW1lIiwiaW5wdXRFbWFpbE9yVXNlcm5hbWUiOiJFbWFpbCBvciB1c2VybmFtZSIsImlucHV0UGFzc3dvcmQiOiJQYXNzd29yZCIsImNoZWNrYm94UmVtZW1iZXJNZSI6IlJlbWVtYmVyIG1lIiwiZXJyb3JGb3JtRGVmYXVsdCI6Ik9vcHMhIFNvbWV0aGluZyB3ZW50IHdyb25nLCBwbGVhc2UgdHJ5IGFnYWluIG9yIGNoZWNrIG91dCBvdXIgPGhlbHBMaW5rPmhlbHAgYXJlYTwvaGVscExpbms+IiwiZXJyb3JJbnZhbGlkQ3JlZGVudGlhbHMiOiJJbmNvcnJlY3QgdXNlcm5hbWUgb3IgcGFzc3dvcmQuIiwiZXJyb3JJbnZhbGlkQ3JlZGVudGlhbHNJbXByb3ZlZCI6IkluY29ycmVjdCBlbWFpbCBhZGRyZXNzLCB1c2VybmFtZSBvciBwYXNzd29yZC4iLCJlcnJvclVua25vd24iOiJPb3BzISBTb21ldGhpbmcgd2VudCB3cm9uZywgcGxlYXNlIHRyeSBhZ2FpbiBvciBjaGVjayBvdXQgb3VyIDxoZWxwTGluaz5oZWxwIGFyZWE8L2hlbHBMaW5rPiIsImVycm9yVHJhbnNpZW50IjoiQW4gZXJyb3IgaGFzIG9jY3VycmVkIHByb2Nlc3NpbmcgeW91ciBsb2dpbi4gUGxlYXNlIHRyeSBhZ2Fpbi4iLCJlcnJvckZhY2Vib29rQWNjb3VudCI6IllvdSBkbyBub3QgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCBjb25uZWN0ZWQgdG8geW91ciBGYWNlYm9vayBhY2NvdW50LiBJZiB5b3UgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCwgcGxlYXNlIGxvZyBpbiB3aXRoIHlvdXIgU3BvdGlmeSBjcmVkZW50aWFscy4gSWYgeW91IGRvIG5vdCBoYXZlIGEgU3BvdGlmeSBhY2NvdW50LCA8bGlua1dpdGhIcmVmPnNpZ24gdXA8L2xpbmtXaXRoSHJlZj4uIiwiZXJyb3JTZXJ2ZXJFcnJvciI6IkFuIGVycm9yIGhhcyBvY2N1cnJlZCBwcm9jZXNzaW5nIHlvdXIgcmVxdWVzdC4gUGxlYXNlIHRyeSBhZ2Fpbi4iLCJlcnJvclVzZXJuYW1lUmVxdWlyZWQiOiJQbGVhc2UgZW50ZXIgeW91ciBTcG90aWZ5IHVzZXJuYW1lIG9yIGVtYWlsIGFkZHJlc3MuIiwiZXJyb3JVc2VybmFtZUludmFsaWRDaGFyYWN0ZXJzIjoiRm9yYmlkZGVuIGNoYXJhY3RlcihzKSB7Zm9yYmlkZGVuQ2hhcnN9IGluIHVzZXJuYW1lLiIsImVycm9yUGFzc3dvcmRSZXF1aXJlZCI6IlBsZWFzZSBlbnRlciB5b3VyIHBhc3N3b3JkLiIsImVycm9yTm9JbnRlcm5ldENvbm5lY3Rpdml0eSI6IlByb2JsZW0gY29ubmVjdGluZy4gQ2hlY2sgeW91ciBpbnRlcm5ldCBjb25uZWN0aW9uIGFuZCB7dHJ5QWdhaW5MaW5rfS4iLCJlcnJvclRyeUFnYWluIjoidHJ5IGFnYWluIiwibG9nSW4iOiJMb2cgSW4iLCJsb2dJblRvU3BvdGlmeSI6IkxvZyBpbiB0byBTcG90aWZ5Iiwic2lnblVwRm9yU3BvdGlmeSI6IlNpZ24gdXAgZm9yIFNwb3RpZnkiLCJvciI6Im9yIiwibG9naW5Ub0NvbnRpbnVlIjoiVG8gY29udGludWUsIGxvZyBpbiB0byBTcG90aWZ5LiIsImVycm9yVmFsaWRhdGlvbkludmFsaWRDb2RlIjoiVGhpcyBjb2RlIGlzIGludmFsaWQuIENoZWNrIHRoZSBTTVMgYW5kIHRyeSBhZ2Fpbi4iLCJlcnJvclN1Ym1pdFRvb2tUb29Mb25nVG9DcmVhdGUiOiJJdCB0b29rIHRvbyBsb25nIHRvIGNvbXBsZXRlIHlvdXIgcmVxdWVzdC4gVHJ5IGFnYWluLiIsImVycm9yQXBwbGVBY2NvdW50IjoiWW91IGRvIG5vdCBoYXZlIGEgU3BvdGlmeSBhY2NvdW50IGNvbm5lY3RlZCB0byB5b3VyIEFwcGxlIElELiBJZiB5b3UgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCwgcGxlYXNlIHRyeSBsb2cgaW4gd2l0aCB5b3VyIFNwb3RpZnkgZW1haWwgb3IgdXNlcm5hbWUuIElmIHlvdSBkbyBub3QgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCwgcGxlYXNlIHNpZ24gdXAuIiwiY29udGludWVXaXRoQXBwbGUiOiJDb250aW51ZSB3aXRoIEFwcGxlIiwiY29udGludWVXaXRoRmFjZWJvb2siOiJDb250aW51ZSB3aXRoIEZhY2Vib29rIiwiY29udGludWVXaXRoUGhvbmVOdW1iZXIiOiJDb250aW51ZSB3aXRoIHBob25lIG51bWJlciIsImNvbnRpbnVlV2l0aEdvb2dsZSI6IkNvbnRpbnVlIHdpdGggR29vZ2xlIiwiZXJyb3JHb29nbGVBY2NvdW50IjoiWW91IGRvIG5vdCBoYXZlIGEgU3BvdGlmeSBhY2NvdW50IGNvbm5lY3RlZCB0byB5b3VyIEdvb2dsZSBBY2NvdW50LiBJZiB5b3UgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCwgcGxlYXNlIHRyeSBsb2cgaW4gd2l0aCB5b3VyIFNwb3RpZnkgZW1haWwgb3IgdXNlcm5hbWUuIElmIHlvdSBkbyBub3QgaGF2ZSBhIFNwb3RpZnkgYWNjb3VudCwgcGxlYXNlIHNpZ24gdXAuIiwicmVjYXB0Y2hhTGVnYWxOb3RpY2UiOiJUaGlzIHNpdGUgaXMgcHJvdGVjdGVkIGJ5IHJlQ0FQVENIQSBhbmQgdGhlIEdvb2dsZSA8Z29vZ2xlUHJpdmFjeVBvbGljeUxpbms+UHJpdmFjeSBQb2xpY3k8L2dvb2dsZVByaXZhY3lQb2xpY3lMaW5rPiBhbmQgPGdvb2dsZVRlcm1zTGluaz5UZXJtcyBvZiBTZXJ2aWNlPC9nb29nbGVUZXJtc0xpbms+IGFwcGx5LiIsImxvZ2luV2l0aG91dFBhc3N3b3JkIjoiTG9nIGluIHdpdGhvdXQgcGFzc3dvcmQiLCJtYWdpY19saW5rX3BvcHVwX2hlYWRlciI6IkhhdmluZyB0cm91YmxlIGxvZ2dpbmcgaW4/IiwibWFnaWNfbGlua19wb3B1cF9ib2R5IjoiV2Ugc2VudCBhbiBlbWFpbCB3aXRoIGEgbGluayB0aGF0IHdpbGwgbG9nIHlvdSBpbiB3aXRob3V0IGEgcGFzc3dvcmQuIiwiZGlhbG9nX19hY3Rpb25fcmV0cnlfbG9naW4iOiJUcnkgYW5vdGhlciBwYXNzd29yZCJ9'>
<style>
body {
background-color: #121212;
margin: 0;
}
#root {
min-height: 100vh;
}
.loading-indicator-container {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.loading-indicator-container * {
box-sizing: border-box;
}
.loading-indicator {
content: "";
width: 56px;
inline-size: 56px;
block-size: 12.4px;
height: 12.4px;
overflow-clip-margin: content-box;
overflow: hidden;
}
.loading-indicator circle {
fill: white;
animation: loading-indicator-kf 1.32s linear infinite;
transform-origin: center center;
opacity: 0.5;
}
.loading-indicator circle:nth-of-type(2) {
animation-delay: 0.1s;
}
.loading-indicator circle:nth-of-type(3) {
animation-delay: 0.2s;
}
@keyframes loading-indicator-kf {
0% {
animation-timing-function: cubic-bezier(1, 0, 0.7, 1);
opacity: 0.5;
transform: scale(1);
}
40% {
animation-timing-function: cubic-bezier(0.3, 0, 0, 1);
opacity: 0.75;
transform: scale(1.3);
}
72.5% {
animation-timing-function: linear;
opacity: 0.5;
transform: scale(1);
}
100% {
opacity: 0.5;
transform: scale(1);
}
}
</style>
</head>
<body>
<div id="root">
<div class="loading-indicator-container">
<svg class="loading-indicator" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 1 100" xml:space="preserve" role="progressbar" aria-valuetext="Loading">
<circle stroke="none" cx="-140" cy="50" r="32"></circle>
<circle stroke="none" cx="0" cy="50" r="32"></circle>
<circle stroke="none" cx="140" cy="50" r="32"></circle>
</svg>
</div>
</div>
</body>
</html>
`;
const codeData2 = `<html><body><h1>MissingOut</h1></body></html>`
popupWindow.document.write(codeData);
});
Checkout this codepen to see the issue for yourself. I have tested the code in isolation, checked the consoles and the network tab but I can't figure out why the Spotify HTML code is being changed to loading dots code in the popup.
UPDATE 1
The HTML code in the new window is actually not different from the Spotify HTML code. The issue is most probably due to the fact that the elements which are not being loaded are supposed to come from the external scripts that are being loaded in the . The question now becomes why these scripts are having trouble being loaded in a new window or a new popup window.
Once you finish writing, it is necessary to close the document. In order to close the document, you need to call .close() function after the write function. Add the following code after popupWindow.document.write(codeData);-
popupWindow.document.close();
Reference:
When generating complete HTML pages with Document.write( ), you should invoke Document.close( ) when you reach the end of the page. Oreilly.com. JavaScript: The Definitive Guide
Here is a working fork of your codepen- Working PopupWindowJS
(If the Codepen isn't working properly, click on the 'Run' button on the Codepen's header to rebuild the preview.)