htmx.org 1.9.4 → 1.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/htmx.js CHANGED
@@ -60,6 +60,7 @@ return (function () {
60
60
  settlingClass:'htmx-settling',
61
61
  swappingClass:'htmx-swapping',
62
62
  allowEval:true,
63
+ allowScriptTags:true,
63
64
  inlineScriptNonce:'',
64
65
  attributesToSettle:["class", "style", "width", "height"],
65
66
  withCredentials:false,
@@ -73,6 +74,7 @@ return (function () {
73
74
  getCacheBusterParam: false,
74
75
  globalViewTransitions: false,
75
76
  methodsThatUseUrlParams: ["get"],
77
+ selfRequestsOnly: false
76
78
  },
77
79
  parseInterval:parseInterval,
78
80
  _:internalEval,
@@ -84,7 +86,7 @@ return (function () {
84
86
  sock.binaryType = htmx.config.wsBinaryType;
85
87
  return sock;
86
88
  },
87
- version: "1.9.4"
89
+ version: "1.9.6"
88
90
  };
89
91
 
90
92
  /** @type {import("./htmx").HtmxInternalApi} */
@@ -304,6 +306,7 @@ return (function () {
304
306
  case "th":
305
307
  return parseHTML("<table><tbody><tr>" + resp + "</tr></tbody></table>", 3);
306
308
  case "script":
309
+ case "style":
307
310
  return parseHTML("<div>" + resp + "</div>", 1);
308
311
  default:
309
312
  return parseHTML(resp, 0);
@@ -571,9 +574,17 @@ return (function () {
571
574
  }
572
575
  }
573
576
 
577
+ function startsWith(str, prefix) {
578
+ return str.substring(0, prefix.length) === prefix
579
+ }
580
+
581
+ function endsWith(str, suffix) {
582
+ return str.substring(str.length - suffix.length) === suffix
583
+ }
584
+
574
585
  function normalizeSelector(selector) {
575
586
  var trimmedSelector = selector.trim();
576
- if (trimmedSelector.startsWith("<") && trimmedSelector.endsWith("/>")) {
587
+ if (startsWith(trimmedSelector, "<") && endsWith(trimmedSelector, "/>")) {
577
588
  return trimmedSelector.substring(1, trimmedSelector.length - 2);
578
589
  } else {
579
590
  return trimmedSelector;
@@ -593,6 +604,8 @@ return (function () {
593
604
  return [document];
594
605
  } else if (selector === 'window') {
595
606
  return [window];
607
+ } else if (selector === 'body') {
608
+ return [document.body];
596
609
  } else {
597
610
  return getDocument().querySelectorAll(normalizeSelector(selector));
598
611
  }
@@ -1344,7 +1357,7 @@ return (function () {
1344
1357
  var verb, path;
1345
1358
  if (elt.tagName === "A") {
1346
1359
  verb = "get";
1347
- path = elt.href; // DOM property gives the fully resolved href of a relative link
1360
+ path = getRawAttribute(elt, 'href')
1348
1361
  } else {
1349
1362
  var rawAttribute = getRawAttribute(elt, "method");
1350
1363
  verb = rawAttribute ? rawAttribute.toLowerCase() : "get";
@@ -1821,7 +1834,7 @@ return (function () {
1821
1834
  }
1822
1835
 
1823
1836
  function evalScript(script) {
1824
- if (script.type === "text/javascript" || script.type === "module" || script.type === "") {
1837
+ if (htmx.config.allowScriptTags && (script.type === "text/javascript" || script.type === "module" || script.type === "") ) {
1825
1838
  var newScript = getDocument().createElement("script");
1826
1839
  forEach(script.attributes, function (attr) {
1827
1840
  newScript.setAttribute(attr.name, attr.value);
@@ -1860,12 +1873,25 @@ return (function () {
1860
1873
  }
1861
1874
 
1862
1875
  function findHxOnWildcardElements(elt) {
1863
- if (!document.evaluate) return []
1876
+ var node = null
1877
+ var elements = []
1878
+
1879
+ if (document.evaluate) {
1880
+ var iter = document.evaluate('//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") ]]', elt)
1881
+ while (node = iter.iterateNext()) elements.push(node)
1882
+ } else {
1883
+ var allElements = document.getElementsByTagName("*")
1884
+ for (var i = 0; i < allElements.length; i++) {
1885
+ var attributes = allElements[i].attributes
1886
+ for (var j = 0; j < attributes.length; j++) {
1887
+ var attrName = attributes[j].name
1888
+ if (startsWith(attrName, "hx-on:") || startsWith(attrName, "data-hx-on:")) {
1889
+ elements.push(allElements[i])
1890
+ }
1891
+ }
1892
+ }
1893
+ }
1864
1894
 
1865
- let node = null
1866
- const elements = []
1867
- const iter = document.evaluate('//*[@*[ starts-with(name(), "hx-on:") or starts-with(name(), "data-hx-on:") ]]', elt)
1868
- while (node = iter.iterateNext()) elements.push(node)
1869
1895
  return elements
1870
1896
  }
1871
1897
 
@@ -1925,25 +1951,29 @@ return (function () {
1925
1951
  function addHxOnEventHandler(elt, eventName, code) {
1926
1952
  var nodeData = getInternalData(elt);
1927
1953
  nodeData.onHandlers = [];
1928
- var func = new Function("event", code + "; return;");
1954
+ var func;
1929
1955
  var listener = function (e) {
1930
- return func.call(elt, e);
1956
+ return maybeEval(elt, function() {
1957
+ if (!func) {
1958
+ func = new Function("event", code);
1959
+ }
1960
+ func.call(elt, e);
1961
+ });
1931
1962
  };
1932
1963
  elt.addEventListener(eventName, listener);
1933
1964
  nodeData.onHandlers.push({event:eventName, listener:listener});
1934
- return {nodeData:nodeData, code:code, func:func, listener:listener};
1935
1965
  }
1936
1966
 
1937
1967
  function processHxOn(elt) {
1938
1968
  var hxOnValue = getAttributeValue(elt, 'hx-on');
1939
- if (hxOnValue && htmx.config.allowEval) {
1969
+ if (hxOnValue) {
1940
1970
  var handlers = {}
1941
1971
  var lines = hxOnValue.split("\n");
1942
1972
  var currentEvent = null;
1943
1973
  var curlyCount = 0;
1944
1974
  while (lines.length > 0) {
1945
1975
  var line = lines.shift();
1946
- var match = line.match(/^\s*([a-zA-Z:\-]+:)(.*)/);
1976
+ var match = line.match(/^\s*([a-zA-Z:\-\.]+:)(.*)/);
1947
1977
  if (curlyCount === 0 && match) {
1948
1978
  line.split(":")
1949
1979
  currentEvent = match[1].slice(0, -1); // strip last colon
@@ -1967,10 +1997,10 @@ return (function () {
1967
1997
  for (var i = 0; i < elt.attributes.length; i++) {
1968
1998
  var name = elt.attributes[i].name
1969
1999
  var value = elt.attributes[i].value
1970
- if (name.startsWith("hx-on:") || name.startsWith("data-hx-on:")) {
2000
+ if (startsWith(name, "hx-on:") || startsWith(name, "data-hx-on:")) {
1971
2001
  let eventName = name.slice(name.indexOf(":") + 1)
1972
2002
  // if the eventName starts with a colon, prepend "htmx" for shorthand support
1973
- if (eventName.startsWith(":")) eventName = "htmx" + eventName
2003
+ if (startsWith(eventName, ":")) eventName = "htmx" + eventName
1974
2004
 
1975
2005
  addHxOnEventHandler(elt, eventName, value)
1976
2006
  }
@@ -2119,7 +2149,7 @@ return (function () {
2119
2149
  eventResult = eventResult && elt.dispatchEvent(kebabedEvent)
2120
2150
  }
2121
2151
  withExtensions(elt, function (extension) {
2122
- eventResult = eventResult && (extension.onEvent(eventName, event) !== false)
2152
+ eventResult = eventResult && (extension.onEvent(eventName, event) !== false && !event.defaultPrevented)
2123
2153
  });
2124
2154
  return eventResult;
2125
2155
  }
@@ -2199,7 +2229,13 @@ return (function () {
2199
2229
  // so we can prevent privileged data entering the cache.
2200
2230
  // The page will still be reachable as a history entry, but htmx will fetch it
2201
2231
  // live from the server onpopstate rather than look in the localStorage cache
2202
- var disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]');
2232
+ var disableHistoryCache
2233
+ try {
2234
+ disableHistoryCache = getDocument().querySelector('[hx-history="false" i],[data-hx-history="false" i]')
2235
+ } catch (e) {
2236
+ // IE11: insensitive modifier not supported so fallback to case sensitive selector
2237
+ disableHistoryCache = getDocument().querySelector('[hx-history="false"],[data-hx-history="false"]')
2238
+ }
2203
2239
  if (!disableHistoryCache) {
2204
2240
  triggerEvent(getDocument().body, "htmx:beforeHistorySave", {path: path, historyElt: elt});
2205
2241
  saveToHistoryCache(path, cleanInnerHtmlForHistory(elt), getDocument().title, window.scrollY);
@@ -2212,7 +2248,7 @@ return (function () {
2212
2248
  // remove the cache buster parameter, if any
2213
2249
  if (htmx.config.getCacheBusterParam) {
2214
2250
  path = path.replace(/org\.htmx\.cache-buster=[^&]*&?/, '')
2215
- if (path.endsWith('&') || path.endsWith("?")) {
2251
+ if (endsWith(path, '&') || endsWith(path, "?")) {
2216
2252
  path = path.slice(0, -1);
2217
2253
  }
2218
2254
  }
@@ -2308,7 +2344,20 @@ return (function () {
2308
2344
  return indicators;
2309
2345
  }
2310
2346
 
2311
- function removeRequestIndicatorClasses(indicators) {
2347
+ function disableElements(elt) {
2348
+ var disabledElts = findAttributeTargets(elt, 'hx-disabled-elt');
2349
+ if (disabledElts == null) {
2350
+ disabledElts = [];
2351
+ }
2352
+ forEach(disabledElts, function (disabledElement) {
2353
+ var internalData = getInternalData(disabledElement);
2354
+ internalData.requestCount = (internalData.requestCount || 0) + 1;
2355
+ disabledElement.setAttribute("disabled", "");
2356
+ });
2357
+ return disabledElts;
2358
+ }
2359
+
2360
+ function removeRequestIndicators(indicators, disabled) {
2312
2361
  forEach(indicators, function (ic) {
2313
2362
  var internalData = getInternalData(ic);
2314
2363
  internalData.requestCount = (internalData.requestCount || 0) - 1;
@@ -2316,6 +2365,13 @@ return (function () {
2316
2365
  ic.classList["remove"].call(ic.classList, htmx.config.requestClass);
2317
2366
  }
2318
2367
  });
2368
+ forEach(disabled, function (disabledElement) {
2369
+ var internalData = getInternalData(disabledElement);
2370
+ internalData.requestCount = (internalData.requestCount || 0) - 1;
2371
+ if (internalData.requestCount === 0) {
2372
+ disabledElement.removeAttribute('disabled');
2373
+ }
2374
+ });
2319
2375
  }
2320
2376
 
2321
2377
  //====================================================================
@@ -2591,37 +2647,37 @@ return (function () {
2591
2647
  if (swapInfo) {
2592
2648
  var split = splitOnWhitespace(swapInfo);
2593
2649
  if (split.length > 0) {
2594
- swapSpec["swapStyle"] = split[0];
2595
- for (var i = 1; i < split.length; i++) {
2596
- var modifier = split[i];
2597
- if (modifier.indexOf("swap:") === 0) {
2598
- swapSpec["swapDelay"] = parseInterval(modifier.substr(5));
2599
- }
2600
- if (modifier.indexOf("settle:") === 0) {
2601
- swapSpec["settleDelay"] = parseInterval(modifier.substr(7));
2602
- }
2603
- if (modifier.indexOf("transition:") === 0) {
2604
- swapSpec["transition"] = modifier.substr(11) === "true";
2605
- }
2606
- if (modifier.indexOf("scroll:") === 0) {
2607
- var scrollSpec = modifier.substr(7);
2650
+ for (var i = 0; i < split.length; i++) {
2651
+ var value = split[i];
2652
+ if (value.indexOf("swap:") === 0) {
2653
+ swapSpec["swapDelay"] = parseInterval(value.substr(5));
2654
+ } else if (value.indexOf("settle:") === 0) {
2655
+ swapSpec["settleDelay"] = parseInterval(value.substr(7));
2656
+ } else if (value.indexOf("transition:") === 0) {
2657
+ swapSpec["transition"] = value.substr(11) === "true";
2658
+ } else if (value.indexOf("ignoreTitle:") === 0) {
2659
+ swapSpec["ignoreTitle"] = value.substr(12) === "true";
2660
+ } else if (value.indexOf("scroll:") === 0) {
2661
+ var scrollSpec = value.substr(7);
2608
2662
  var splitSpec = scrollSpec.split(":");
2609
2663
  var scrollVal = splitSpec.pop();
2610
2664
  var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null;
2611
2665
  swapSpec["scroll"] = scrollVal;
2612
2666
  swapSpec["scrollTarget"] = selectorVal;
2613
- }
2614
- if (modifier.indexOf("show:") === 0) {
2615
- var showSpec = modifier.substr(5);
2667
+ } else if (value.indexOf("show:") === 0) {
2668
+ var showSpec = value.substr(5);
2616
2669
  var splitSpec = showSpec.split(":");
2617
2670
  var showVal = splitSpec.pop();
2618
2671
  var selectorVal = splitSpec.length > 0 ? splitSpec.join(":") : null;
2619
2672
  swapSpec["show"] = showVal;
2620
2673
  swapSpec["showTarget"] = selectorVal;
2621
- }
2622
- if (modifier.indexOf("focus-scroll:") === 0) {
2623
- var focusScrollVal = modifier.substr("focus-scroll:".length);
2674
+ } else if (value.indexOf("focus-scroll:") === 0) {
2675
+ var focusScrollVal = value.substr("focus-scroll:".length);
2624
2676
  swapSpec["focusScroll"] = focusScrollVal == "true";
2677
+ } else if (i == 0) {
2678
+ swapSpec["swapStyle"] = value;
2679
+ } else {
2680
+ logError('Unknown modifier in hx-swap: ' + value);
2625
2681
  }
2626
2682
  }
2627
2683
  }
@@ -2844,6 +2900,27 @@ return (function () {
2844
2900
  return arr;
2845
2901
  }
2846
2902
 
2903
+ function verifyPath(elt, path, requestConfig) {
2904
+ var sameHost
2905
+ var url
2906
+ if (typeof URL === "function") {
2907
+ url = new URL(path, document.location.href);
2908
+ var origin = document.location.origin;
2909
+ sameHost = origin === url.origin;
2910
+ } else {
2911
+ // IE11 doesn't support URL
2912
+ url = path
2913
+ sameHost = startsWith(path, document.location.origin)
2914
+ }
2915
+
2916
+ if (htmx.config.selfRequestsOnly) {
2917
+ if (!sameHost) {
2918
+ return false;
2919
+ }
2920
+ }
2921
+ return triggerEvent(elt, "htmx:validateUrl", mergeObjects({url: url, sameHost: sameHost}, requestConfig));
2922
+ }
2923
+
2847
2924
  function issueAjaxRequest(verb, path, elt, event, etc, confirmed) {
2848
2925
  var resolve = null;
2849
2926
  var reject = null;
@@ -2860,12 +2937,30 @@ return (function () {
2860
2937
  var responseHandler = etc.handler || handleAjaxResponse;
2861
2938
 
2862
2939
  if (!bodyContains(elt)) {
2863
- return; // do not issue requests for elements removed from the DOM
2940
+ // do not issue requests for elements removed from the DOM
2941
+ maybeCall(resolve);
2942
+ return promise;
2864
2943
  }
2865
2944
  var target = etc.targetOverride || getTarget(elt);
2866
2945
  if (target == null || target == DUMMY_ELT) {
2867
2946
  triggerErrorEvent(elt, 'htmx:targetError', {target: getAttributeValue(elt, "hx-target")});
2868
- return;
2947
+ maybeCall(reject);
2948
+ return promise;
2949
+ }
2950
+
2951
+ var eltData = getInternalData(elt);
2952
+ var submitter = eltData.lastButtonClicked;
2953
+
2954
+ if (submitter) {
2955
+ var buttonPath = getRawAttribute(submitter, "formaction");
2956
+ if (buttonPath != null) {
2957
+ path = buttonPath;
2958
+ }
2959
+
2960
+ var buttonVerb = getRawAttribute(submitter, "formmethod")
2961
+ if (buttonVerb != null) {
2962
+ verb = buttonVerb;
2963
+ }
2869
2964
  }
2870
2965
 
2871
2966
  // allow event-based confirmation w/ a callback
@@ -2875,12 +2970,12 @@ return (function () {
2875
2970
  }
2876
2971
  var confirmDetails = {target: target, elt: elt, path: path, verb: verb, triggeringEvent: event, etc: etc, issueRequest: issueRequest};
2877
2972
  if (triggerEvent(elt, 'htmx:confirm', confirmDetails) === false) {
2878
- return;
2973
+ maybeCall(resolve);
2974
+ return promise;
2879
2975
  }
2880
2976
  }
2881
2977
 
2882
2978
  var syncElt = elt;
2883
- var eltData = getInternalData(elt);
2884
2979
  var syncStrategy = getClosestAttributeValue(elt, "hx-sync");
2885
2980
  var queueStrategy = null;
2886
2981
  var abortable = false;
@@ -2896,10 +2991,12 @@ return (function () {
2896
2991
  syncStrategy = (syncStrings[1] || 'drop').trim();
2897
2992
  eltData = getInternalData(syncElt);
2898
2993
  if (syncStrategy === "drop" && eltData.xhr && eltData.abortable !== true) {
2899
- return;
2994
+ maybeCall(resolve);
2995
+ return promise;
2900
2996
  } else if (syncStrategy === "abort") {
2901
2997
  if (eltData.xhr) {
2902
- return;
2998
+ maybeCall(resolve);
2999
+ return promise;
2903
3000
  } else {
2904
3001
  abortable = true;
2905
3002
  }
@@ -2943,7 +3040,8 @@ return (function () {
2943
3040
  issueAjaxRequest(verb, path, elt, event, etc)
2944
3041
  });
2945
3042
  }
2946
- return;
3043
+ maybeCall(resolve);
3044
+ return promise;
2947
3045
  }
2948
3046
  }
2949
3047
 
@@ -3072,6 +3170,12 @@ return (function () {
3072
3170
  }
3073
3171
  }
3074
3172
 
3173
+ if (!verifyPath(elt, finalPath, requestConfig)) {
3174
+ triggerErrorEvent(elt, 'htmx:invalidPath', requestConfig)
3175
+ maybeCall(reject);
3176
+ return promise;
3177
+ };
3178
+
3075
3179
  xhr.open(verb.toUpperCase(), finalPath, true);
3076
3180
  xhr.overrideMimeType("text/html");
3077
3181
  xhr.withCredentials = requestConfig.withCredentials;
@@ -3103,7 +3207,7 @@ return (function () {
3103
3207
  var hierarchy = hierarchyForElt(elt);
3104
3208
  responseInfo.pathInfo.responsePath = getPathFromResponse(xhr);
3105
3209
  responseHandler(elt, responseInfo);
3106
- removeRequestIndicatorClasses(indicators);
3210
+ removeRequestIndicators(indicators, disableElts);
3107
3211
  triggerEvent(elt, 'htmx:afterRequest', responseInfo);
3108
3212
  triggerEvent(elt, 'htmx:afterOnLoad', responseInfo);
3109
3213
  // if the body no longer contains the element, trigger the event on the closest parent
@@ -3129,21 +3233,21 @@ return (function () {
3129
3233
  }
3130
3234
  }
3131
3235
  xhr.onerror = function () {
3132
- removeRequestIndicatorClasses(indicators);
3236
+ removeRequestIndicators(indicators, disableElts);
3133
3237
  triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
3134
3238
  triggerErrorEvent(elt, 'htmx:sendError', responseInfo);
3135
3239
  maybeCall(reject);
3136
3240
  endRequestLock();
3137
3241
  }
3138
3242
  xhr.onabort = function() {
3139
- removeRequestIndicatorClasses(indicators);
3243
+ removeRequestIndicators(indicators, disableElts);
3140
3244
  triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
3141
3245
  triggerErrorEvent(elt, 'htmx:sendAbort', responseInfo);
3142
3246
  maybeCall(reject);
3143
3247
  endRequestLock();
3144
3248
  }
3145
3249
  xhr.ontimeout = function() {
3146
- removeRequestIndicatorClasses(indicators);
3250
+ removeRequestIndicators(indicators, disableElts);
3147
3251
  triggerErrorEvent(elt, 'htmx:afterRequest', responseInfo);
3148
3252
  triggerErrorEvent(elt, 'htmx:timeout', responseInfo);
3149
3253
  maybeCall(reject);
@@ -3155,6 +3259,7 @@ return (function () {
3155
3259
  return promise
3156
3260
  }
3157
3261
  var indicators = addRequestIndicatorClasses(elt);
3262
+ var disableElts = disableElements(elt);
3158
3263
 
3159
3264
  forEach(['loadstart', 'loadend', 'progress', 'abort'], function(eventName) {
3160
3265
  forEach([xhr, xhr.upload], function (target) {
@@ -3259,6 +3364,7 @@ return (function () {
3259
3364
  var xhr = responseInfo.xhr;
3260
3365
  var target = responseInfo.target;
3261
3366
  var etc = responseInfo.etc;
3367
+ var requestConfig = responseInfo.requestConfig;
3262
3368
 
3263
3369
  if (!triggerEvent(elt, 'htmx:beforeOnLoad', responseInfo)) return;
3264
3370
 
@@ -3282,16 +3388,17 @@ return (function () {
3282
3388
  return;
3283
3389
  }
3284
3390
 
3391
+ var shouldRefresh = hasHeader(xhr, /HX-Refresh:/i) && "true" === xhr.getResponseHeader("HX-Refresh");
3392
+
3285
3393
  if (hasHeader(xhr, /HX-Redirect:/i)) {
3286
3394
  location.href = xhr.getResponseHeader("HX-Redirect");
3395
+ shouldRefresh && location.reload();
3287
3396
  return;
3288
3397
  }
3289
3398
 
3290
- if (hasHeader(xhr,/HX-Refresh:/i)) {
3291
- if ("true" === xhr.getResponseHeader("HX-Refresh")) {
3292
- location.reload();
3293
- return;
3294
- }
3399
+ if (shouldRefresh) {
3400
+ location.reload();
3401
+ return;
3295
3402
  }
3296
3403
 
3297
3404
  if (hasHeader(xhr,/HX-Retarget:/i)) {
@@ -3307,12 +3414,14 @@ return (function () {
3307
3414
  var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204;
3308
3415
  var serverResponse = xhr.response;
3309
3416
  var isError = xhr.status >= 400;
3310
- var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo);
3417
+ var ignoreTitle = htmx.config.ignoreTitle
3418
+ var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError, ignoreTitle:ignoreTitle }, responseInfo);
3311
3419
  if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return;
3312
3420
 
3313
3421
  target = beforeSwapDetails.target; // allow re-targeting
3314
3422
  serverResponse = beforeSwapDetails.serverResponse; // allow updating content
3315
3423
  isError = beforeSwapDetails.isError; // allow updating error
3424
+ ignoreTitle = beforeSwapDetails.ignoreTitle; // allow updating ignoring title
3316
3425
 
3317
3426
  responseInfo.target = target; // Make updated target available to response events
3318
3427
  responseInfo.failed = isError; // Make failed property available to response events
@@ -3338,6 +3447,10 @@ return (function () {
3338
3447
  }
3339
3448
  var swapSpec = getSwapSpecification(elt, swapOverride);
3340
3449
 
3450
+ if (swapSpec.hasOwnProperty('ignoreTitle')) {
3451
+ ignoreTitle = swapSpec.ignoreTitle;
3452
+ }
3453
+
3341
3454
  target.classList.add(htmx.config.swappingClass);
3342
3455
 
3343
3456
  // optional transition API promise callbacks
@@ -3431,7 +3544,7 @@ return (function () {
3431
3544
  }
3432
3545
  }
3433
3546
 
3434
- if(settleInfo.title) {
3547
+ if(settleInfo.title && !ignoreTitle) {
3435
3548
  var titleElt = find("title");
3436
3549
  if(titleElt) {
3437
3550
  titleElt.innerHTML = settleInfo.title;
@@ -3582,9 +3695,22 @@ return (function () {
3582
3695
  //====================================================================
3583
3696
  // Initialization
3584
3697
  //====================================================================
3698
+ var isReady = false
3699
+ getDocument().addEventListener('DOMContentLoaded', function() {
3700
+ isReady = true
3701
+ })
3585
3702
 
3703
+ /**
3704
+ * Execute a function now if DOMContentLoaded has fired, otherwise listen for it.
3705
+ *
3706
+ * This function uses isReady because there is no realiable way to ask the browswer whether
3707
+ * the DOMContentLoaded event has already been fired; there's a gap between DOMContentLoaded
3708
+ * firing and readystate=complete.
3709
+ */
3586
3710
  function ready(fn) {
3587
- if (getDocument().readyState !== 'loading') {
3711
+ // Checking readyState here is a failsafe in case the htmx script tag entered the DOM by
3712
+ // some means other than the initial page load.
3713
+ if (isReady || getDocument().readyState === 'complete') {
3588
3714
  fn();
3589
3715
  } else {
3590
3716
  getDocument().addEventListener('DOMContentLoaded', fn);
Binary file