Skip to content

Commit

Permalink
v1.5.1 release candidate (#353)
Browse files Browse the repository at this point in the history
* Use  to schedule staticRender() iff jQuery 3 or higher is present in shinyMode

* Bump version; update NEWS

* Prevent staticRender from being called unconditionally when htmlwidgets.js is loaded after page load

* Add a comment; abstract out duplication in logic (#354)
  • Loading branch information
jcheng5 authored Oct 7, 2019
1 parent 4944b75 commit 91cfa1c
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 31 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: htmlwidgets
Type: Package
Title: HTML Widgets for R
Version: 1.5.0.9000
Version: 1.5.1
Authors@R: c(
person("Ramnath", "Vaidyanathan", role = c("aut", "cph")),
person("Yihui", "Xie", role = c("aut")),
Expand Down
5 changes: 5 additions & 0 deletions inst/NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
htmlwidgets 1.5.1
-------------------------------------------------------

* Fixed an issue with dynamically rendered widgets (i.e., using `shiny::uiOutput()` to render a widget) with any version of shiny prior to 1.4. This issue was introduced by htmlwidgets 1.5. (#351)

htmlwidgets 1.5
-----------------------------------------------------------------------

Expand Down
72 changes: 42 additions & 30 deletions inst/www/htmlwidgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -659,44 +659,56 @@
invokePostRenderHandlers();
}

// Wait until after the document has loaded to render the widgets.
if (shinyMode && window.jQuery) {
/*
/ Shiny 1.4.0 bumps jQuery from 1.x to 3.x, which means jQuery's
/ on-ready handler (i.e., $(fn)) is now asyncronous (i.e., it now
/ really means $(setTimeout(fn)). https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous
/
/ In order to ensure the order of execution of on-ready handlers
/ remains consistent with how it's been in the past, a static render
/ in Shiny is now scheduled via $().
/
/ This is not a long term solution: it just preserves the current order
/ of execution. Part of that ordering is to ensure initShiny executes
/ _after_ staticRender, which is both right and wrong:
/ * It's wrong because, when initShiny executes, it registers methods
/ like Shiny.onInputChange that widget authors would expect to be available
/ during a staticRender.
/ * It's also 'right' because initShiny currently (as of v1.4.0) wants
/ to execute after user code so that it can bind to any dynamically
/ created elements.
/
/ A longer term solution might be to make changes to Shiny so that
/ these methods are available before the binding takes place, and then
/ the ordering would be: register methods -> static render -> bind.
*/
window.jQuery(function() {

function has_jQuery3() {
if (!window.jQuery) {
return false;
}
var $version = window.jQuery.fn.jquery;
var $major_version = parseInt($version.split(".")[0]);
return $major_version >= 3;
}

/*
/ Shiny 1.4 bumped jQuery from 1.x to 3.x which means jQuery's
/ on-ready handler (i.e., $(fn)) is now asyncronous (i.e., it now
/ really means $(setTimeout(fn)).
/ https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous
/
/ Since Shiny uses $() to schedule initShiny, shiny>=1.4 calls initShiny
/ one tick later than it did before, which means staticRender() is
/ called renderValue() earlier than (advanced) widget authors might be expecting.
/ https://github.com/rstudio/shiny/issues/2630
/
/ For a concrete example, leaflet has some methods (e.g., updateBounds)
/ which reference Shiny methods registered in initShiny (e.g., setInputValue).
/ Since leaflet is privy to this life-cycle, it knows to use setTimeout() to
/ delay execution of those methods (until Shiny methods are ready)
/ https://github.com/rstudio/leaflet/blob/18ec981/javascript/src/index.js#L266-L268
/
/ Ideally widget authors wouldn't need to use this setTimeout() hack that
/ leaflet uses to call Shiny methods on a staticRender(). In the long run,
/ the logic initShiny should be broken up so that method registration happens
/ right away, but binding happens later.
*/
function maybeStaticRenderLater() {
if (shinyMode && has_jQuery3()) {
window.jQuery(window.HTMLWidgets.staticRender);
} else {
window.HTMLWidgets.staticRender();
});
} else if (document.addEventListener) {
}
}

if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", function() {
document.removeEventListener("DOMContentLoaded", arguments.callee, false);
window.HTMLWidgets.staticRender();
maybeStaticRenderLater();
}, false);
} else if (document.attachEvent) {
document.attachEvent("onreadystatechange", function() {
if (document.readyState === "complete") {
document.detachEvent("onreadystatechange", arguments.callee);
window.HTMLWidgets.staticRender();
maybeStaticRenderLater();
}
});
}
Expand Down

0 comments on commit 91cfa1c

Please sign in to comment.