Office.js nullifies browser history functions breaking history usage
Asked Answered
T

4

26

The official version of office.js available here:

https://appsforoffice.microsoft.com/lib/1/hosted/office.js

It contains the following lines in code:

window.history.replaceState = null;
window.history.pushState = null;

This breaks some of the history functionality in my Excel Add-ins (I'm using react and react-router)

Why is office.js nullifying those history functions? I cannot find any explanation in the documentation.

Tsingyuan answered 7/3, 2017 at 7:45 Comment(2)
Let me follow up on that with the teamBoozer
See Michael's comment below and my updated answer -- Microsoft is finally fixing this.Worlock
A
24

This works for me - cache the objects before office-js deletes them:

<script type="text/javascript">
    // Office js deletes window.history.pushState and window.history.replaceState. Cache them and restore them
    window._historyCache = {
        replaceState: window.history.replaceState,
        pushState: window.history.pushState
    };
</script>

<script type="text/javascript" src="https://appsforoffice.microsoft.com/lib/1/hosted/office.js"></script>

<script type="text/javascript">
    // Office js deletes window.history.pushState and window.history.replaceState. Restore them
    window.history.replaceState = window._historyCache.replaceState;
    window.history.pushState = window._historyCache.pushState;
</script>
Adytum answered 7/12, 2018 at 3:16 Comment(2)
Thanks man! Works with vue 2.6.12 and vue-router 3.4.8.Hesperus
Works in Angular 16 with msal-angular 3.0.0. But you should call delete windows._historyCache.replaceState and delete windows._historyCache.puschState when you no longer need themGreenhorn
D
9

The browser control used in Excel does not support History API, if replaceState and pushState were not nulled out they would be available to react but always throw an exception when called. Until a new browser control is available, you will need to switch to hash based routing or use a polyfill for History API. https://github.com/devote/HTML5-History-API seems to work if you include the script reference after office.js.

Dimmick answered 9/3, 2017 at 19:0 Comment(10)
To add to that: will discuss if there is a longer-term fix for this at the platform/framework level. But the polyfill is likely your best short-term solution.Boozer
Hey @MichaelZlatkovsky-Microsoft, is there a list/reference of unsupported browser APIs for all Add-in hosts and platforms?Offbeat
Hi @MichaelZlatkovsky-Microsoft , adding history.js gives several troubles to my app, the recent one is this. So it seems that i have to disable html5mode and thus not need to use history.js in my app. Do you have any update about office.js with regard to html5mode?Icao
No updates, sorry. Under the current infrastructure, the most we could do would simply be to bundle a polyfills like this one with Office.js, but it sounds like it wouldn't actually solve your problem in either case.Boozer
Six years after @Dan Saunders - MSFT comment, Excel now uses latest browsers for the Taskpane and restoring the browser history as per the accepted answer by stepper does not cause any problems that I have found in latest Office 365 Excel. I have, however, not tried to do anything with the browser history in my addin. But if I do not restore it i get endless routing issues with Angular 16 and msal-angular 3.0.0-betaGreenhorn
@MichaelZlatkovsky-Microsoft - Is there any reason the nulling out of these methods can't be removed from Office.js now that Excel's browser is quite up-to-date? The latest version still does this, but there doesn't seem to be any need to anymore...?Worlock
@T.J.Crowder, apologies for the delay, I was on vacation and then out sick. At this point, I'm many years removed from the Office Extensibility team. I suspect that there are some legacy reasons for why the methods are nulled out broadly (e.g., for when the add-ins still run in Excel 2013 or 2016), though you're also probably right that it could (and probably should) be done more conditionally. I can try to pass a note to the right folks via internal channels, but like I said, I left the team 4+ years ago, and so all I can do is pass along the feedback. All the best!Boozer
@MichaelZlatkovsky-Microsoft - Glad you're feeling better. Thanks!Worlock
@T.J.Crowder, not sure if it was due to my internal ping or not, but FYI it appears that a fix is forthcoming and will be rolling to the CDN soon: github.com/OfficeDev/office-js/issues/…Boozer
@MichaelZlatkovsky-Microsoft - Good news, thanks! And thanks for the internal ping, maybe it was the final push!Worlock
W
1

Microsoft tried to fix (unbreak) this1 (thank you Michael Zlatkovski), but sadly had to roll it back2 as the fix caused unexpected issues. They're going to try adding it as an opt-in.

In the meantime: Since they do this by assigning null to history.pushState and history.replaceState, which shadows the inherited methods with null properties, the easiest way to undo it is to remove those null properties:

delete history.pushState; // Remove 'own' property with `null`, reveals original inherited one
delete history.replaceState;

(If you're using TypeScript, you'll have to disable the error you get on that — the properties aren't optional, so TypeScript won't let you remove them. It doesn't know we're not really removing them.)

Removing the own properties that were added via assignment reveals the inherited original properties.

It may seem counter-intuitive to remove something to restore it, but Office.js isn't removing them, it's just shadowing (hiding) them, akin to this:

class FakeHistory {
    pushState() {
        console.log("Hi, I'm `pushState`");
    }
    replaceState() {
        console.log("Hi, I'm `replaceState`");
    }
}

const history = new FakeHistory();

console.log("Quick test of the 'original' methods:");
history.pushState();
history.replaceState();

console.log("...'Remove' them the way Office.js does...");
history.pushState = null;
history.replaceState = null;

console.log("Can't use them anymore:");
try {
    history.pushState();
    history.replaceState();
} catch (error) {
    console.error(error);
}

console.log("Restore them by getting the own properties out of the way:");
delete history.pushState;
delete history.replaceState;

console.log("We can use the 'original' methods again:");
history.pushState();
history.replaceState();
.as-console-wrapper {
    max-height: 100% !important;
}

1 (link):

gergzk commented Feb 29 2024 5:44 p.m. GMT

Good news! We've recently made the fix discussed above (just removed the overrides). Expect to see it roll out in the newest office.js versions in about a week or two.

2 (link):

nikhilatsap commented Apr 2, 2024, 11:52 AM GMT+1

Hey @gergzk this error is still there in the office.js hosted at /lib/1 CDN url


gergzk commented Apr 2, 2024, 11:35 PM GMT+1

@nikhilatsap - yes, we had to roll back the fix because it had other unwanted side effects. We'll look into adding it as an opt-in.

Worlock answered 23/1 at 15:20 Comment(2)
Apparently they needed to roll it back again, according to gergzk: github.com/OfficeDev/office-js/issues/…Gauche
@AndreasO - Thanks for letting me know! That's too bad. I've updated the answer.Worlock
A
0

My Windows version is 10 Pro, default browser is Edge 42.17134.1.0 . But the right side bar where Outlook runs add-in uses the old IE10 engine ;( (IE10 as a browser also is in Windows). I don't know this is true for all Windows or it's some specific case for my version. IE10 supports history.replaceState and history.pushState, but inside Outlook I have problems with these methods, so simple restore doesn't work for me.

The simple solution with cache history.replaceState and history.pushState doesn't work fore me. In Outlook with IE10 inside I have some unexpected error when my code call history.replaceState or history.pushState. But I found one interesting thing. If suppress the error they do their work.

So my workaround is:

 function isIE10 () {
      return !!document.documentMode
    }

    // Office js deletes window.history.pushState and window.history.replaceState. Cache them and restore them
    // Also there is an issue in Windows Outlook with `pushState` and `replaceState`. They throw an error but in the same time do their expected work
    // So I suppress errors for IE10 (we use it inside Window Outlook)
    window._historyCache = {
      replaceState: function (originalReplaceState) {
        return function () {
          try {
            return originalReplaceState.apply(window.history, arguments)
          } catch (e) {
            if (isIE10()) {
              console.warn("Unexpected error in 'window.history.replaceState', but we can continue to work :)");
              return false;
            }
            throw(e);
          }
        }
      }(window.history.replaceState),
      pushState: function (originalFunction) {
        return function () {
          try {
            return originalFunction.apply(window.history, arguments)
          } catch (e) {
            if (isIE10()) {
              console.warn("Unexpected error in 'window.history.pushState', but we can continue to work :)");
              return false;
            }
            throw(e);
          }
        }
      }(window.history.pushState)
    };

      // In Window Outlook we have issue with 'replaceState' and 'pushState. So replaced it by wrapped version.
      window.history.replaceState = window._historyCache.replaceState;
      window.history.pushState = window._historyCache.pushState;

//include the main code with react-router
//include Office.js


   Office.initialize = function () {

    // Office js deletes window.history.pushState and window.history.replaceState. Restore them
    window.history.replaceState = window._historyCache.replaceState;
    window.history.pushState = window._historyCache.pushState;

    // Now you can start initialize&&run your application
        ....
   }

Note: I should replace history.replaceState and history.pushState before the running any code which work with this API. In my case it's react-router.

Azilian answered 9/4, 2019 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.