-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: made head replacement only target dynamic elements
This avoids a FOUC (Flash of Unstyled Content) and unnecessary resource re-requests on subsequent loads. Fixes #182.
- Loading branch information
1 parent
fb00daf
commit 73aa387
Showing
2 changed files
with
25 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,27 @@ | ||
use wasm_bindgen::JsCast; | ||
use web_sys::Element; | ||
|
||
/// Replaces the current document `<head>` after the delimiter comment | ||
/// with something new. | ||
/// | ||
/// This will only touch the dynamic elements, thus avoiding re-requesting | ||
/// any resources references in the component of the `<head>` shared between | ||
/// all pages, which may create a flash of unstyled content. | ||
pub(crate) fn replace_head(new: &str) { | ||
let document = web_sys::window().unwrap().document().unwrap(); | ||
// Get the current head | ||
let head_elem = web_sys::window() | ||
.unwrap() | ||
.document() | ||
.unwrap() | ||
.query_selector("head") | ||
.unwrap() | ||
let head_node = document.query_selector("head").unwrap().unwrap(); | ||
let head_elem: Element = head_node.unchecked_into(); | ||
// Get everything after the dummy `<meta>` tag we use as a delimiter | ||
let els_to_remove = document | ||
.query_selector_all(r#"meta[itemprop='__perseus_head_boundary'] ~ *"#) | ||
.unwrap(); | ||
let head_html = head_elem.inner_html(); | ||
// We'll assume that there's already previously interpolated head in | ||
// addition to the hardcoded stuff, but it will be separated by the | ||
// server-injected delimiter comment | ||
// Thus, we replace the stuff after that delimiter comment with the | ||
// new head | ||
let head_parts: Vec<&str> = head_html | ||
.split("<!--PERSEUS_INTERPOLATED_HEAD_BEGINS-->") | ||
.collect(); | ||
let new_head = format!( | ||
"{}\n<!--PERSEUS_INTERPOLATED_HEAD_BEGINS-->\n{}", | ||
head_parts[0], new | ||
); | ||
head_elem.set_inner_html(&new_head); | ||
// For some horrific reason, this isn't implemented as an iterator in `web_sys` | ||
for idx in 0..(els_to_remove.length()) { | ||
let el = els_to_remove.get(idx).unwrap(); | ||
head_elem.remove_child(&el); | ||
} | ||
// And now append the new HTML to the head (yes, this position is untyped...) | ||
// This position is inside the element, after its last child | ||
head_elem.insert_adjacent_html("beforeend", new); | ||
} |