Running something only when a page is newly made, not when going back from another page - jquery-mobile

Demo
There is something about the pagecreate event, used with ajax loading, which I do not understand.
The problem is on event firing on multi-page back-and-forth transitions.
// only on page a
$(document).on("pagecreate", function (e) {
alert('on pagecreate\n' + $(e.target).attr('id') )
});
// only on page a, ends
// each page
page a is <div data-role="page" id="page-a">
page b is <div data-role="page" id="page-b">
page c is <div data-role="page" id="page-c">
-
1. p) Load a -> alert a (expected)
q) a go to b -> alert b (expected)
r) b back to a -> no alert on a (good)
2. s) Load a -> alert a (expected)
t) a go to b -> alert b (expected)
u) b go to c -> alert c (expected)
v) c back to b -> alert b (unexpected)
w) b back to a -> no alert on a (strange)
Strange?
v) is strange: why r) does not alert but v) does?
w) is even stranger: why v) alerts but w) does not?
The Question - How to do the following?
3. (Here in 3., you type in the URL of a only once.
NO refresh/F5 key in the whole process)
.
Load a -> alert a
a go to b -> alert b
b go to c -> alert c
.
c back to b -> no alert
b back to a -> no alert
.
a go to b -> alert b
b back to a -> no alert
.
a go to b -> alert b
b go to c -> alert c
c go to d -> alert d
.
d back to c -> no alert
c back to b -> no alert
That is, alert only when a page is newly made. Going back never alerts.

Update
If you want to run code only in forward navigation listen to either pagecontainerbeforechange or pagecontainerbeforetransition and retrieve navigation direction from options object.
I will use beforetransition because it fires once upon navigating, unlike beforechange which fires twice.
If options.direction doesn't return back, run your code as follows.
$(document).on("pagecontainerbeforetransition", function (e, data) {
if ( typeof data.options.direction != "undefined" && data.options.direction != "back" ) {
$(document).one("pagecontainershow", function (e, data) {
alert(data.toPage[0].id);
});
}
});
I used pagecontainershow one time only to show alert. Use it to run any function you want.
Because you are using a Single Page Model, each page div is in a separate file. The default behavior of jQuery Mobile in SPM is that external page are removed from DOM once you navigate away off them. Page "a" is landing page, it is cached and never removed.
When navigate from "a" to "b" you get an alert in "b". When you navigate from "b" to "c", "b" is removed from DOM and "c" is loaded and created. You got an alert there too. When you navigate back to "b" from "c", "c" is removed and "b" is loaded and created. Navigating back to "a" doesn't reload it because it is cached (landing page).
If you want external pages to remain in DOM, cache them as in this answer to one of your recent questions.
Or give each external page div a unique ID and run pagecreate one time .one().
$(document).one("pagecreate", "#pageB", function (e) {
alert("created: " + this.id);
});

Related

Is it possible to drive Elm app with existing navigation constructed outside of the Elm app?

I'm working with an existing Rails app where the navigation must continue to be constructed on the backend (due to complexity and time limitations). The intended result is to have some of the pages generated with Elm, and some with Rails, using no hashes, and no full page reloads (at least for the Elm pages). The simplified version of the navigation looks like this:
<nav>
<a href="rails-page-1">...
<a href="rails-page-2">...
<a href="elm-page-1">...
<a href="elm-page-2">...
</nav>
<div id="elm-container"></div>
I've experimented with the Elm navigation package, and the elm-route-url, possibly coming close with the latter unless I'm fundamentally misunderstanding the package's capability.
Is there a way to accomplish this? I've gotten it working using hash tags, but no luck without them.
using hash tags
Well you got a chuckle out of me.
I have this guy in my Helpers.elm file that I can use in lieu of Html.Events.click.
{-| Useful for overriding the default `<a>` behavior which
causes a refresh, but can be used anywhere
-}
overrideClick : a -> Attribute a
overrideClick =
Decode.succeed
>> onWithOptions "click"
{ stopPropagation = False
, preventDefault = True
}
So on an a [ overrideClick (NavigateTo "/route"), href "/route" ] [ text "link" ] which would allow middle-clicking the element as well as using push state to update the navigation.
What you're needing is something similar on the JavaScript that works with pushState, and you don't want to ruin the middle-click experience. You can hijack all <a>s,preventDefault on its event to stop the browser from navigating, and push in the new state via the target's href. You can delegate the navigation's <a>s in the event handler. Since the Navigation runtime doesn't support listening to history changes externally (rather it appears to be using an effect manager), you'll have to push the value through a port -- luckily if you're using the Navigation package, you should already have the pieces.
On the Elm end, use UrlParser.parsePath in conjuction with one of the Navigation programs. Create a port to subscribe to using the same message that is used for it's internal url changes.
import Navigation exposing (Location)
port externalPush : (Location -> msg) -> Sub msg
type Msg
= UrlChange Location
| ...
main =
Navigation.program UrlChange
{ ...
, subscriptions : \_ -> externalPush UrlChange
}
After the page load, use this:
const hijackNavClick = (event) => {
// polyfill `matches` if needed
if (event.target.matches("a[href]")) {
// prevent the browser navigation
event.preventDefault()
// push the new url
window.history.pushState({}, "", event.target.href)
// send the new location into the Elm runtime via port
// assuming `app` is the name of `Elm.Main.embed` or
// whatever
app.ports.externalPush.send(window.location)
}
}
// your nav selector here
const nav = document.querySelector("nav")
nav.addEventListener("click", hijackNavClick, false)

WebSharper JQuery Mobile Page Hidden

I'm trying to put together a JQuery Mobile site using the F# WebSharper Framework. In WebSharper parlance I have created a Pagelet using JQueryMObile controls which is being served by a Sitelet. Everything compiles and runs, the problem is in the html that is generated.
The page I have declared (simplePage) clearly exists in the markup and is marked with the JQueryMobile css class ui-active to make it visible. However, it is surrounded by a div that is also a page but is not marked with the active css class, making it invisible. Therefore my page inside this div is hidden. I am not creating this containing page div. It seems to be a side-affect of loading the JQueryMobile script in the html head. How can I get rid of it?
I have taken the example from http://websharper.com/samples/JQueryMobile. I am using WebSharper version 2.5.125.62 and WebSharper.JQueryMobile version 2.5.4.198. I have one relevant code file shown below followed by the generated html.
Main:
open IntelliFactory.Html
open IntelliFactory.WebSharper
open IntelliFactory.WebSharper.Html
open IntelliFactory.WebSharper.JQuery
open IntelliFactory.WebSharper.Sitelets
type Action = | Home
[<JavaScript>]
module MyJQueryContent =
let Main() =
JQuery.Mobile.Mobile.Use()
let page = Div [
Id "simplePage"
HTML5.Attr.Data "role" "page"
HTML5.Attr.Data "url" "#simplePage"
] -< [
Div[Text "content"]
]
Div [page]
|>! OnAfterRender (fun _ -> JQuery.Of(page.Body)
|> JQuery.Mobile.JQuery.Page
|> ignore
JQuery.Mobile.Mobile.Instance.ChangePage(JQuery.Of(page.Body)))
[<Sealed>]
type MyJQueryMobileEntryPoint() =
inherit Web.Control()
[<JavaScript>]
override this.Body = MyJQueryContent.Main() :> _
module Pages =
let HomePage =
PageContent <| fun context ->
{ Page.Default with
Title = Some "Index"
Body = [IntelliFactory.Html.Tags.Div[new MyJQueryMobileEntryPoint()]] }
[<Sealed>]
type Website() =
interface IWebsite<Action> with
member this.Sitelet = Sitelet.Content "/" Home Pages.HomePage
member this.Actions = [Home]
type Global() =
inherit System.Web.HttpApplication()
member g.Application_Start(sender: obj, args: System.EventArgs) =
()
[<assembly: Website(typeof<Website>)>]
do ()
Output:
As a side note, you are creating one of the containing DIVs when you write Div [page] instead of page before adding OnAfterRender, but indeed this is not your problem.
As Omar describes, with jQuery Mobile you need to carefully control when and how the initialization of the page structure takes place, especially when working with dynamic pages. I recall seeing your exact problem before but I can't find that conversation in my email box, however, here is an earlier article that has some useful bits for tapping into JQM page initialization:
http://fpish.net/blog/JankoA/id/3362/201367-jquery-mobile-page-reuse-with-websharper
When jQuery Mobile framework is loaded, it checks if there is a page div in DOM. If not, it wraps body's content in page div. To control this behavior, you need to prevent jQM's autoInitializePage in order to initialize manually $.mobile.initializePage() whenever you want.
You need to listen to mobileinit event to override autoInitializePage. The code should be placed after jQuery (core) and before jQuery Mobile libraries in head.
/* jQuery.js */
$(document).on("mobileinit", function () {
$.mobile.autoInitializePage = false;
});
/* jQuery-Mobile.js */
Now you can initialize jQuery Mobile whenever you want by calling $.mobile.initializePage().
I am a developer of WebSharper. Omar is right, this is a JQM thing, another workaround for it is having a dummy page node in the sitelet. Like this:
module Pages =
open IntelliFactory.Html
let HomePage =
PageContent <| fun context ->
{ Page.Default with
Title = Some "Index"
Body =
[
Div [HTML5.Data "role" "page"; Id "dummy"]
Div [new MyJQueryMobileEntryPoint()]
] }

Alert dialog not working in Lua

I am using Lua with Gideros and having alert box to show up when back button is pressed.According to Gideros documentation, when first button is pressed, it returns index 1 but it does not really seem to work this way.I tested the app on my android phone.I realized the oncomplete function is not getting called at all, since I tried using a print statement and even it was not executed, so any idea why is is not getting called?
local function onKeyDown(event)
if event.keyCode == KeyCode.BACK then
local alertDialog = AlertDialog.new("Confirmation", "Are you sure you want to exit?", "Cancel", "Yes")
alertDialog:show()
stage:addEventListener(Event.COMPLETE, oncomplete)
end
end
function oncomplete(e)
if e.buttonIndex == 1 then
stage:addEventListener(Event.APPLICATION_SUSPEND, suspend)
application: exit()
end
end
function suspend()
application: exit()
end
-- key events are dispatched to all Sprite instances on the scene tree (similar to mouse and touch events)
stage:addEventListener(Event.KEY_DOWN, onKeyDown)
As per the conversation the issue was that the event listener for the alert box close event was being attached to the stage instead of the alert dialog.
stage:addEventListener(Event.COMPLETE, oncomplete)
instead of
alertDialog:addEventListener(Event.COMPLETE, oncomplete)

jquery mobile function calling two times

I am using change page event in jquery mobile with multiview plugin when page is change the function calling two times means if am using alert it shows same alert two times and when I click on back button some it shows the page but when I do this frequently it shows error in plug in file can any one help me
Do you mean the pagebeforechange event?
This event is triggering twice as designed.
The difference between this two calls is the data's toPage attribute:
jQuery(document).bind('pagebeforechange', function(e, data) {
var toPage = data.toPage;
if(typeof toPage === 'string') {
// -- triggered first
// -- toPage is a string containing the page`s url
} else {
// -- triggered second
// -- toPage is an jQuery collection object containing the page
}
});

AJAX getting called, even though doc is not loaded

I have this chunk of code in my coffeescript:
$('.asdasd').ready ->
$.ajax '/splunk/100000000',
type: 'GET'
cache: false
success: (html) ->
$('.splunk_results').append html
The asdasd div doesn't even exist -- however, in my console, I can see a call to /splunk/10000000" being made. Why is this happening?
EDIT:
I think the issue might have to do with the fact that the div in question isn't loaded with the initial page -- the page is full of partials, and the div is only loaded with the click on another js button that modifies the DOM. I basically want to see when that div exists, and when it does, make a new request and populate the div with the results of that request.
(heavily edited in response to question edit)
.ready is only a valid event for document. jQuery's documentation for ready doesn't define behavior for cases where the argument to $ isn't document. That said, there's nothing stopping you from defining an event that acts the way you want! Rather than listening for the ready event, invent a custom event (say, readyForSplunk) and trigger it at the appropriate time.
The document, or some nearer parent of the to-be-created asdasd div, should have a delegate handler listening for the readyForSplunk event. The js button that creates the asdasd div should also triggerHandler('readyForSplunk') the new div.
ready runs when the DOM is ready. If you only would like it to run if the element in question is in the page, you could do something like this instead:
_get = ->
$.ajax '/splunk/100000000',
type: 'GET'
cache: false
success: (html) ->
$('.splunk_results').append html
$('.asdasd').each ->
_get()
_get = ->

Resources