Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Sugarcube and jQuery mobile popup

Sugarcube 2.11
Twine 1.4
jQuery Mobile 1.4.5
jquery 1.12.4

I am using the function in this thread (https://twinery.org/forum/discussion/5574/include-external-javascript-scripts-sugarcube) to load external js and css files. I know this script works and that the file paths are correct because 10 different jQuery mobile widgets work just fine.

However, the jQuery Mobile popup widget has an apparently unique problem. When I visit the passage containing the popup, the widget works as expected. If I move away from that passage and then come back, the popup no longer works. By "return," I mean that I have tried <<back>>, <<return>>, and a direct link to return to the passage, and the popup widget fails every time. The only thing that works to "reactivate" the popup is CTRL-R (in Firefox) to reload the page. I also know that the code I am using for the popup widget is correct because I have created a stand-alone HTML file with the same external files and the same widget code and the popup works as expected. Clearly, I'm missing something.

What can I do to make the jQuery Mobile popup widget work reliably upon repeat visits to a passage?

Comments

  • 1. SugarCube v2.11.0 includes jQuery v3.1.0. You should not be pulling in an older version of jQuery.

    2. Show some code. We can't help you solve your issue unless we know what you're actually doing. A description is insufficient in most cases and all but absolutely so when dealing with libraries.
  • Macro in script tagged passage:
    Macro.add("loadExternals", {
        handler  : function () 
    	{
    (function ($) {
    	$(document.head)
    		.append('<script id="jquery-min" type="text/javascript" src="js-css/jquery-1.12.4.min.js"></script>')
    		.append('<link id="jquery-mobile-style" rel="stylesheet" type="text/css" href="js-css/jquery.mobile-1.4.5.min.css" />')
    		.append('<script id="jquery-mobile" type="text/javascript" src="js-css/jquery.mobile-1.4.5.min.js"></script>')
    		.append('<link rel="stylesheet" href="js-css/jquery-ui.css">')
    		.append('<script id="jquery-ui-min" type="text/javascript" src="js-css/jquery-ui.min.js"></script>');
    })(jQuery);
    	}
    });
    

    As for your point #1... I am aware that jQuery v3 is included in SugarCube, and, therefore, was surprised to find that if I don't append jQuery-1.12.4.min.js, then, when I access a passage containing a jQuery Mobile widget (a) the following error is generated: "Error: TypeError: a.event.props is undefined." and (b) the popup widget does not work even on first access to the passage. In the latter case, what I see, using the code directly below this paragraph, is exactly what you would expect to see were all the data- attributes removed. Importantly, though, the tabs widget continues to work perfectly if I omit jQuery-1.12.4.min.js. The problem appears to be unique to the popup widget.

    Here is the code in the passage containing the tooltip:
    <<loadExternals>>\
    
    <p>A paragraph with a tooltip. <a href="#popupInfo" data-rel="popup" data-position-to="#passages" data-transition="flip">Learn more</a></p>
    <div data-role="popup" id="popupInfo" class="ui-content" data-theme="a" style="max-width:600px;"><p>Lorem ipsum dolor sit amet.</p></div>
    


    This is the code for the jQuery Mobile tabs widget, which works just fine:
    <<loadExternals>>\
    <<script>>
      $( function() {
        $( "#tabs" ).tabs({
    		collapsible: true
    });
      } );
    <</script>>
    
    <div id="tabs">
      <ul>
        <li><a href="#tabs-1">Nunc tincidunt</a></li>
        <li><a href="#tabs-2">Proin dolor</a></li>
      </ul>
      <div id="tabs-1">
        <p>Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus.</p>
      </div>
      <div id="tabs-2">
        <p>Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis.</p>
      </div>
    

    As a final note, the situation is not improved if I remove everything else from the script tagged passage except for the loadExternals macro. Also, if I don't include the loadExternals macro in EACH passage containing a mobile widget, the widget in that passage won't work.

  • rickrawson wrote: »
    Macro in script tagged passage:
    Nope. Full stop. You're appending new elements to the document head each and every time you invoke <<loadExternals>>—that's a bad thing. You do not want that code in a macro and you certainly do not want to call it multiple times. Place the code within a script section, so it is called exactly once. I'd also suggest cleaning up the IDs and providing one for all of the elements—the jQuery UI CSS element is missing one. (code example below)

    rickrawson wrote: »
    As for your point #1... I am aware that jQuery v3 is included in SugarCube, and, therefore, was surprised to find that if I don't append jQuery-1.12.4.min.js, […]
    That would be because jQuery Mobile, which is part of the jQuery Foundation no less, cannot seem to maintain compatibility with jQuery itself on anything remotely resembling even a glacially slow timescale. That is an extremely sad state of affairs.

    That said, at least use the latest in the jQuery v2.1 series (v2.1.4) which is compatible with jQuery Mobile v1.4.5. This is important as jQuery v1.x is not guaranteed to be compatible with SugarCube v2.

    rickrawson wrote: »
    Also, if I don't include the loadExternals macro in EACH passage containing a mobile widget, the widget in that passage won't work.
    That's because you're attempting to treat a web application like it was a web page, which it is not. To make your tabs work properly, you need to use either one of the post-action task objects or the PassageDone special passage. I'd suggest the postdisplay task object, since you're already using JavaScript. (code example below)


    JavaScript Example
    The following has been tested with the markup shown below and should be all you need—aside from the libraries; no <<loadExternals>> macro, no <<script>> invocations on passages with tabs. It includes both the code to load the libraries and a postdisplay task to enable tabs. (Twine 1: goes in your script-tagged passage; Twine 2: goes in Story JavaScript)
    (function ($) {
    	/*
    		Tested with:
    			jQuery (v2.1.4)
    			jQuery Mobile (v1.4.5)
    			jQuery UI (v1.11.4)
    	*/
    	$(document.head)
    		.append('<script id="user-jquery-js" type="text/javascript" src="js-css/jquery-2.1.4.min.js"></script>')
    		.append('<link id="user-jquery-mobile-css" rel="stylesheet" type="text/css" href="js-css/jquery.mobile-1.4.5.min.css">')
    		.append('<script id="user-jquery-mobile-js" type="text/javascript" src="js-css/jquery.mobile-1.4.5.min.js"></script>')
    		.append('<link id="user-jquery-ui-css" rel="stylesheet" href="js-css/jquery-ui.css">')
    		.append('<script id="user-jquery-ui-js" type="text/javascript" src="js-css/jquery-ui.min.js"></script>');
    })(jQuery);
    
    postdisplay['tab-handling'] = function () {
    	var $tabs = $("#tabs");
    
    	if ($tabs.length > 0) {
    		$tabs.tabs({ collapsible: true });
    	}
    };
    


    CSS Example
    Beyond the JavaScript, I'd suggest using something like the following style, if you're not already. It will remove the external link symbol from the popup and tab links. (Twine 1: goes in your stylesheet-tagged passage; Twine 2: goes in Story Stylesheet)
    #story a.link-external:after {
    	content: none;
    }
    


    Tested Markup Examples
    <p>A paragraph with a tooltip. <a href="#popupInfo" data-rel="popup" data-position-to="#passages" data-transition="flip">Learn more</a></p>
    <div data-role="popup" id="popupInfo" class="ui-content" data-theme="a" style="max-width:600px;"><p>Lorem ipsum dolor sit amet.</p></div>
    
    <div id="tabs">
      <ul>
        <li><a href="#tabs-1">Nunc tincidunt</a></li>
        <li><a href="#tabs-2">Proin dolor</a></li>
      </ul>
      <div id="tabs-1">
        <p>Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus.</p>
      </div>
      <div id="tabs-2">
        <p>Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis.</p>
      </div>
    </div>
    
    NOTE: Your sample of the tabs markup was missing the final closing </div>. I'm unsure if that was simply a copy/paste snafu or it's missing in your working markup as well, but you might want to look into that.
  • First, let me say that I REALLY appreciate the time and effort you have already donated to help out. I created a new .tws file and then copy/pasted your code into it. Here is the twee file output:
    :: Start
    
    [[popup]]
    
    [[tabs]]
    
    
    :: myScripts [script]
    Config.debug = false;
    
    
    (function ($) {
    	/*
    		Tested with:
    			jQuery (v2.1.4)
    			jQuery Mobile (v1.4.5)
    			jQuery UI (v1.11.4)
    	*/
    	$(document.head)
    		.append('<script id="user-jquery-js" type="text/javascript" src="js-css/jquery-2.1.4.min.js"></script>')
    		.append('<link id="user-jquery-mobile-css" rel="stylesheet" type="text/css" href="js-css/jquery.mobile-1.4.5.min.css">')
    		.append('<script id="user-jquery-mobile-js" type="text/javascript" src="js-css/jquery.mobile-1.4.5.min.js"></script>')
    		.append('<link id="user-jquery-ui-css" rel="stylesheet" href="js-css/jquery-ui.css">')
    		.append('<script id="user-jquery-ui-js" type="text/javascript" src="js-css/jquery-ui.min.js"></script>');
    })(jQuery);
    
    
    postdisplay['tab-handling'] = function () {
    	var $tabs = $("#tabs");
    
    	if ($tabs.length > 0) {
    		$tabs.tabs({ collapsible: true });
    	}
    };
    
    
    :: myStylesheet [stylesheet]
    #story a.link-external:after {
    	content: none;
    }
    
    
    :: popup
    <p>A paragraph with a tooltip. <a href="#popupInfo" data-rel="popup" data-position-to="#passages" data-transition="flip">Learn more</a></p>
    <div data-role="popup" id="popupInfo" class="ui-content" data-theme="a" style="max-width:600px;"><p>Lorem ipsum dolor sit amet.</p></div>
    
    <<return>>
    
    
    :: tabs
    <div id="tabs">
      <ul>
        <li><a href="#tabs-1">Nunc tincidunt</a></li>
        <li><a href="#tabs-2">Proin dolor</a></li>
      </ul>
      <div id="tabs-1">
        <p>Proin elit arcu, rutrum commodo, vehicula tempus, commodo a, risus.</p>
      </div>
      <div id="tabs-2">
        <p>Morbi tincidunt, dui sit amet facilisis feugiat, odio metus gravida ante, ut pharetra massa metus id nunc. Duis scelerisque molestie turpis.</p>
      </div>
    </div>
    
    
    <<return>>
    
    
    :: StoryTitle
    Untitled Story
    
    


    The tabs widget works. The popup widget does not... IF I access the popup passage from the Start passage. If I "test play" the popup passage directly, then the popup widget works.
  • I did only test the popup code on the starting passage. Let me check what happens when I move the markup to a another passage. Though, it's probably going to boil down to you were relying on auto-initialization behavior when you should have been calling some API method like with tabs—again, a web page vs. web application issue.

    Yes. It's exactly what I though it was. You need to call its API method post-display, like is being done with the tabs API, to initialize new popups on the page.

    Replace the scripting I previously gave with something like the following:
    (function ($) {
    	/*
    		Tested with:
    			jQuery (v2.1.4)
    			jQuery Mobile (v1.4.5)
    			jQuery UI (v1.11.4)
    	*/
    	$(document.head)
    		.append('<script id="user-jquery-js" type="text/javascript" src="js-css/jquery-2.1.4.min.js"></script>')
    		.append('<link id="user-jquery-mobile-css" rel="stylesheet" type="text/css" href="js-css/jquery.mobile-1.4.5.min.css">')
    		.append('<script id="user-jquery-mobile-js" type="text/javascript" src="js-css/jquery.mobile-1.4.5.min.js"></script>')
    		.append('<link id="user-jquery-ui-css" rel="stylesheet" href="js-css/jquery-ui.css">')
    		.append('<script id="user-jquery-ui-js" type="text/javascript" src="js-css/jquery-ui.min.js"></script>');
    })(jQuery);
    
    postdisplay['jquery-addon-handling'] = function () {
    	var $tabs = $('#tabs');
    
    	if ($tabs.length > 0) {
    		$tabs.tabs({ collapsible : true });
    	}
    
    	var $popups = $('[data-role="popup"]');
    
    	if ($popups.length > 0) {
    		$popups.popup();
    	}
    };
    
  • Thank you!! It works!

    Except, I ran into a problem when I applied the fix to my project...

    I am having an embarrassing problem with relative paths in the jQuery function. The widgets work with these paths:
    (function ($) {
    	$(document.head)
    		.append('<script id="user-jquery-js" type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>')
    		.append('<link id="user-jquery-mobile-css" rel="stylesheet" type="text/css" href="../js-css/jquery.mobile-1.4.5.min.css">')
    		.append('<script id="user-jquery-mobile-js" type="text/javascript" src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>')
    		.append('<link id="user-jquery-ui-css" rel="stylesheet" href="../js-css/jquery-ui.css">')
    		.append('<script id="user-jquery-ui-js" type="text/javascript" src="../js-css/jquery-ui.min.js"></script>');
    })(jQuery);
    

    ...but not with these paths:
    (function ($) {
    	$(document.head)
    		.append('<script id="user-jquery-js" type="text/javascript" src="../js-css/jquery-2.1.4.min.js"></script>')
    		.append('<link id="user-jquery-mobile-css" rel="stylesheet" type="text/css" href="../js-css/jquery.mobile-1.4.5.min.css">')
    		.append('<script id="user-jquery-mobile-js" type="text/javascript" src="../js-css/jquery.mobile-1.4.5.min.js"></script>');
    		.append('<link id="user-jquery-ui-css" rel="stylesheet" href="../js-css/jquery-ui.css">')
    		.append('<script id="user-jquery-ui-js" type="text/javascript" src="../js-css/jquery-ui.min.js"></script>');
    })(jQuery);
    

    ...the difference being that, for the function that works, I'm accessing files via a mixture of http and local files at ../js-css/. Since the ../js-css/*.css files are being accessed in the first version of the function, above, I know the relative paths in the second version (that doesn't work) are correct. If I move the js-css folder so that it is a sub-directory of my project, rather than one folder <i>up</i>, and change the relative paths in the function accordingly, the widgets work. So, I know it isn't a problem with the local .js files <i>per se</i>. None of this makes any sense to me, which is why I'm embarrassed: the solution probably involves something ridiculously simple.

  • You're not providing enough information for anyone to be able to help you. Are you serving these files from a web server? If so, is the js-css directory within its public root?

    Based on those paths your directory and file structure would have to look something like the following:
    js-css/
    	jquery-2.1.4.min.js
    	jquery.mobile-1.4.5.min.css
    	jquery.mobile-1.4.5.min.js
    	jquery-ui.css
    	jquery-ui.min.js
    some-directory/
    	compiled-project.html
    
    Where the parent directory of the compiled HTML file is a sibling of the js-css directory.
  • You have depicted my directory structure correctly.
  • edited November 2016
    Something weird is going on with Twine or my computer or the OS environment or sunspots. Now, my project file cannot even find a storyIncludes file, which is in another directory, something I have not had a problem with until today. I will do some troubleshooting, and return here either with my solution or a specific question for the forum.
Sign In or Register to comment.