10

I'd like to remove the #_=_ artefact that Facebook adds to URLs when a user logs in on my website.

I'm using this script:

if (window.location.hash === '#_=_') {
    const uri = window.location.toString();
    const withNoHash = uri.substring(0, uri.indexOf('#'));
    window.history.replaceState({}, document.title, withNoHash);
}

I'd like the script to fire as soon as possible, so I've put it in the <head> and it seems to work fine on Chrome & Firefox.

Is it standardized that the window.history API is ready when the script executes in <head>?

(and document.title, by the way).

BenMorel
  • 34,448
  • 50
  • 182
  • 322
  • 3
    Frankly, I'm not sure I understand why it is a question whether any portion of the `window` or `document` API _wouldn't_ be ready as soon as JavaScript is being executed. As far as I'm aware, there's no precedent where portions of the API need to be instantiated asynchronously before they can be utilized in code-- it makes sense that these are part of the environment and are present when the environment comes up. Do you have evidence/reason to believe otherwise that inspired this question? – Alexander Nied Nov 07 '20 at 20:44
  • I am not sure I understand your question since being able to execute a script means the full range of web APIs is available in that context. Also, if the script runs currently in chrome and ff, isn't that "good enough"? maybe more context is needed – Peter Nov 07 '20 at 20:47
  • 3
    @AlexanderNied Well `document.head` and `document.body` are famously `null` until they're parsed from the html and instantiated, but that's a part of the DOM. `window` APIs indeed should always be ready, unless documented otherwise. – Bergi Nov 07 '20 at 21:20
  • @all The reason that inspired this question is indeed the DOM API not being ready in ``, hence my concern about `window` APIs. – BenMorel Nov 07 '20 at 22:08
  • Unrelated to the question at hand but would your current script catch the artefact if the user landed on one of your sites that already has a non-fb hash in the uri? – Joe - GMapsBook.com Nov 08 '20 at 18:48
  • @Joe The script only catches the exact artefact in `window.location.hash`, which is fine because I never redirect from FB to a URL that already has a hash. Or did I misunderstand your question? – BenMorel Nov 08 '20 at 19:05
  • If you don’t redirect to a hashed uri then it’s all good. – Joe - GMapsBook.com Nov 08 '20 at 19:35
  • @Bergi - ah, of course; thanks for the clarification! I had never considered that, but it makes perfect sense. This is of course why I am always leveraging [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event), [`$(document).ready`](https://learn.jquery.com/using-jquery-core/document-ready/), or similar protections. I learned these early and they are second nature, but of course they exist because the DOM is not ready/parsed at first JS execution. – Alexander Nied Nov 09 '20 at 14:21
  • What could be "unsafe" about this? There could be scripts that do rely on that hash, or even it could be set by a script running after yours, or anything, but how any of these is "unsafe" is up to you to decide. The title case is about the same, except that the *usual* place to set it is inside the `` tag and that if your script runs before that tag got parsed the tag value will be ignored. But your computer shouldn't blow up anyway. – Kaiido Nov 11 '20 at 08:27

4 Answers4

1

On the topic of window

Standard browsers implement a Window interface, from which a global window property is exposed to javascript in the documents. Subsequent navigation will load different documents in the same Window even if new tabs are opened. So the properties you use, like window.location and window.history inside your document, would be present in the Window before a user navigates to your page (from Facebook) and therefore be available to your document.

This also applies to when you directly load your page in a new browser window - the document will have access to the window property. More on Window and window here: https://developer.mozilla.org/en-US/docs/Web/API/Window

If you are worried about your page getting loaded by a non-standard browser, or for some reason, the window property's history and location properties are overridden, you could just do a check to see if they are available, before calling them with:

if (window && window.location && window.location.hash) {
// safely use window.location.hash here
}

But even then, the error would be suppressed by the browser on the client-side.

On the topic of using document.title with replaceState()

The specification specifies it as a string, so by design, it will return an empty string if it is not set. There are no warnings from Mozilla for using it before a document is fully loaded. More here https://developer.mozilla.org/en-US/docs/Web/API/Document/title

Here are some quick tests I did to see if it is in fact the case using an HTML page with no <title> tag.

<html>
    <head>
        <script>
            console.log("title", document.title)
            window.history.replaceState({}, document.title, "newHMTL.page");
        </script>
    </head>
    <body>
    Testing
    </body>
</html>

There are no errors or warnings as expected.

On the topic of replaceState

The specification points out that most browsers ignore the title / document.title parameter that you pass to replaceState:

Most browsers currently ignore this parameter, although they may use it in the future. Passing the empty string here should be safe against future changes to the method. Alternatively, you could pass a short title for the state.

So while I had a page ready, some more quick tests. Setting the title to null; undefined; and a function; and then passing it to replaceState did not change the title in the history nor throw errors in Chrome when there was a <title> tag or not. So 6 tests.

<html>
    <!-- <title>title</title> -->
    <head>
    <script>
        let title = () => alert("huh?") //null; //undefined;
        console.log("Title", title);
        window.history.replaceState({}, title, "NewHTML.page");
        //works as expected
    </script>
    </head>
    <body>
    Testing
    </body>
</html>
Herald Smit
  • 2,342
  • 1
  • 22
  • 28
  • 1
    The replaceState `title` parameter has no relation with the document's `title`, it just sets the [history entry's `title`](https://html.spec.whatwg.org/multipage/history.html#she-title) – Kaiido Nov 11 '20 at 08:20
  • I may misread your answer, but from here it still sounds like you were expecting it to have a relation with the "/ document.title". – Kaiido Nov 11 '20 at 08:54
  • I can't edit it anymore - made too many edits :\ He passes document.title to replaceState to set the history object's title to the current document.title. The question was if document.title is accessible in the which it is - even as an empty string. – Herald Smit Nov 11 '20 at 09:25
0

Yes it's safe as the window object will be ready when the browser start parsing the head section.

Browser creates window > window loads document object into itself > document object render DOM > document object loads all of its resources > window object fires onload event

The head section is part of the DOM api and Document object is a property of the window object and hence the document will be loaded once the window object is ready. As the history.replaceState is part of window object, it's safe of do any script part in head section.

The Window interface represents a window containing a DOM document; the document property points to the DOM document loaded in that window. A window for a given document can be obtained using the document.defaultView prope

Sohail Ashraf
  • 10,078
  • 2
  • 26
  • 42
  • 2
    How do you know this? Can you reference the W3C specs on this? What's that a quote from? – Liam Nov 10 '20 at 11:07
  • 1
    Actually it's very obvious, the very first thing which the browser creates is the window object and then the url related properties. `The Window object has an associated Document, which is a Document object. It is set when the Window object is created, and only ever changed during navigation from the initial about:blank Document.` reference [https://html.spec.whatwg.org/multipage/window-object.html#the-window-object] – Sohail Ashraf Nov 10 '20 at 11:44
0

It is pretty safe, but I would not use it in development without a DOMContentLoaded listener. Just to be extra safe and account for those who use older browsers, I advise that you add that listener for DOMContentLoaded or window.onload that way you won't encounter any issues on a slower loading page. Also with the window.history API, as it is a native function that requires no separate functionality, you may use that safely and immediately.
TL;DR
Add a DOMContentLoaded listener just in case.

myjobistobehappy
  • 736
  • 5
  • 16
  • The whole point of my question is to *not* wait for `DOMContentLoaded`, as I want to make it disappear ASAP! I'm not sure why you're specifying "I would not use it *in development*"? – BenMorel Nov 14 '20 at 10:56
  • @Benjamin Have you tried not injecting the script into the head, but just running it on the page? That doesn't require a `DOMContentLoaded`, while getting access to the head element might. – myjobistobehappy Nov 14 '20 at 22:06
0

Aloha) Yes, it's safe for you and your clients because window is a global object what is available in browser for everyone and he can be used by any human. and your function can be executed by anyone. Don't worry about security in that case ;)

Pavel
  • 155
  • 1
  • 2