Howdy, Stranger!

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

Text Input Cursor Automatically Placed? (Sugarcube)

edited November 2014 in Help! with 1.x
Is there a way for the text input cursor to automatically be placed in the text input box once a passage loads (so you don't have to click on the text input box to display the text input cursor)? Same way that Google homepage loads the text input cursor upon loading of their page.

Thank You
InsertCoin25

Comments

  • Yes.  There's no automatic way to do that currently, however, so it'll require a little setup to achieve.

    Preface: In SugarCube, the input element macros give each element a unique ID.  Generally, the ID is the name of the macro plus the normalized name of the $variable (normalized here largely means sans the $ and all lowercase); the <<radiobutton>> macro goes a little farther and appends a group ID as well (since radiobuttons are created in groups).  Anyway, what this means is that you should be able to target any input element which was created with the macros easily via its ID.  If you're creating your input element by hand, then I'll assume you know how to target it.

    For example, say that the passage which contained your input macros was titled "Get Names" and that it looked something like this:

    What's your character's name? <<textbox "$pcName" "">>
    What's your character's dog's name? <<textbox "$dogName" "">>
    The created selectors for the input elements would be "#textbox-pcname" and "#textbox-dogname", respectively.

    Then to autofocus the $pcName textbox, you would add something like the following to the PassageDone special passage:

    /% The <<if>> macro is here to ensure that the <<script>> macro is only run with the appropriate passage. %/
    <<if passage() is "Get Names">>\
    <<script>>
    var input = document.querySelector("#textbox-pcname");
    if (input !== null) {
    input.focus();
    }
    <</script>>\
    <</if>>
    Make sense?
  • Thank You TheMadExile. This works great on passages, only problem I am having is that the script won't work on the Start passage. I have used the following code:

    <<if passage() is "Start">>\ --- in a PassageReady, but did not work. Would this render the script before the Start passage loads? If not is there a way to use this script for the Start passage?

    Other than this little quirk everything works perfectly!! Thanks again TheMadExile.

    InsertCoin25
  • Thanks so much for this - it's exactly what I'm looking for!  Having trouble getting it to work though.

    I'm working in 1.3.5. because I need some specific text input functionality which was removed in later versions.

    I have a passage named "begin" with the following content:
    <<textinput $prompt prompt>>
    My PassageDone looks like this:
    /% The <<if>> macro is here to ensure that the <<script>> macro is only run with the appropriate passage. %/

    <<script>>
    <<if passage() is "begin">>\
    var input = document.querySelector("#textinput-prompt");
    if (input !== null) {
    input.focus();
    }
    <</script>>\
    <</if>>
    Does PassageDone need a tag or anything like that?  I just have a passage literally named "PassageDone".

    This isn't giving the focus to the text input field however.  Any help would be MUCH appreciated as I know nothing about Javascript so this is pretty hard to debug!


  • flipLyric wrote:

    Thanks so much for this - it's exactly what I'm looking for!  Having trouble getting it to work though.
    Does PassageDone need a tag or anything like that?  I just have a passage literally named "PassageDone".

    The PassageDone special passage is a feature of the SugarCube story format and does not exist in Sugarcane which I believe you are using by your usage of the <<textinput>> macro.
  • Ah yes - good point.  Any way of accomplishing this in Sugarcane that anyone might know of?
  • InsertCoin25 wrote:

    Thank You TheMadExile. This works great on passages, only problem I am having is that the script won't work on the Start passage. I have used the following code:

    <<if passage() is "Start">>\ --- in a PassageReady, but did not work. Would this render the script before the Start passage loads? If not is there a way to use this script for the Start passage?


    I believe I mentioned that it should go in the PassageDone special passage, not PassageReady.  The element needs to be on the page before it can be focused, and only the PassageDone special passage is run at the appropriate time (i.e. after the element has been placed on the page).


    Here's a quote, originally from SugarCube FAQ (reply #23), on how the order of execution plays out during passage rendering in SugarCube:

    TheMadExile wrote:

    Hanon wrote:

    I don't know what PassageReady/Done really does...I'm guessing PassageReady does things before displaying the text and PassageDone does things after displaying the text?


    Got it in one.

    [quote="SugarCube docs Reserved & Special Names Passage Names"]
    PassageReady: Used for pre-passage-rendering tasks, like redoing dynamic changes (happens just before the rendering of each passage).
    PassageDone: Used for post-passage-rendering tasks, like redoing dynamic changes (happens just after the rendering of each passage).


    They and the prerender/postrender task objects are similar in that they both allow you to do dynamic things around the display of a passage (by navigation, the &lt;&lt;display&gt;&gt; macro does not trigger these).  The main differences being:
    • PassageReady/PassageDone are passages, while prerender/postrender are JavaScript objects used as repositories of functions (which I generally refer to as tasks).
    • PassageReady/PassageDone are executed before/after the passage is rendered, while the functions within the prerender/postrender objects are called during passage render, just before/after the passage content is generated.
    The order of execution typically looks something like this:
    [list type=decimal]
    State history updated for the incoming passage
    PassageReady executed, if not rendering "offscreen"
    Incoming passage is rendered
    [list type=upper-alpha]
    prerender tasks called
    Incoming passage content generated
    postrender tasks called


    Transition from outgoing passage to incoming passage, if not rendering "offscreen"
    PassageDone executed, if not rendering "offscreen"
    Update the UI elements, if not rendering "offscreen"

    Now you know.  And knowing is half the wait, my '80s is showing.
  • flipLyric wrote:

    Ah yes - good point.  Any way of accomplishing this in Sugarcane that anyone might know of?


    I'm unsure if the &lt;&lt;textinput&gt;&gt; macro you're using in Sugarcane provides a convenient ID for selection, so your selector might have to change.  The big issue, however, is that the vanilla story formats have no analog to the PassageDone special passage (and you're using an ancient and broken version anyway).

    Your best bet will probably be to wrap the &lt;History&gt;.display() method.  For example: (goes in a script tagged passage)

    History.prototype.realdisplay = History.prototype.display;
    History.prototype.display = function (/* variadic */) {
    // call this.realdisplay() with the appropriate arguments
    this.realdisplay.apply(this, arguments);

    // now that the passage is on the page, focus the appropriate element
    if (passage() === "begin") {
    var input = document.querySelector("#textinput-prompt");
    if (input !== null) {
    input.focus();
    }
    }
    };
  • Really appreciate that - I will give that a try. 

    Yep, I'm aware that I'm using a version that is both old and broken!  The reason is that my game needs "press enter to submit the content of a text box", that functionality got removed after 1.3.5. and being a non-coder I wasn't able to figure out how to patch it back in.  If you happened to know if that was available in a more recent version of Twine then I could probably move over to that, as I'm still at an early stage.

    Anyway, many thanks for offering help to someone using a daft version and also misunderstanding the thread title!
  • Sadly, I couldn't get that working after trying various permutations of it.

    I tried a different route by editing the Sugarcane header.html target and adding the autofocus property to the text field like this:
    var e ="<input autofocus id='"+d+"' name='boris' type='text' onkeypress=\"var charCode; if(event &amp;&amp; event.which){charCode = event.which;}else if(window.event){event = window.event;charCode = event.keyCode;}if(charCode == 13) {state.history[0].variables."+d+" = this.value; state.display(\'"+c[1]+"\')}\"/>";
    This gives focus to the text field every time one is present when a page loads (which is what I'll want for this game) but frustratingly, the focus disappears soon after!  I've tried a couple of other Javascript methods of giving focus to an element within this file but still couldn't get them to work.  Not really sure what else I should try at this point.
  • Got it working!!! Thanks TheMadExile for helping me out on this. Is there any news related to a new release of Sugarcube and, if so what features are going to be added?

    Thank You
    InsertCoin25
  • flipLyric wrote:

    Sadly, I couldn't get that working after trying various permutations of it.

    I tried a different route by editing the Sugarcane header.html target and adding the autofocus property to the text field like this:

    var e ="<input autofocus id='"+d+"' name='boris' type='text' onkeypress=\"var charCode; if(event &amp;&amp; event.which){charCode = event.which;}else if(window.event){event = window.event;charCode = event.keyCode;}if(charCode == 13) {state.history[0].variables."+d+" = this.value; state.display(\'"+c[1]+"\')}\"/>";


    I believe that the code is sound, so it may be the case that you do need to change the selector.  Ah, yes.  If the code above is representative, then the ID of the &lt;input&gt; element is simply the name of the $variable, sans the sigil ($).

    So, in the code example I gave earlier: (assuming the variable really is named: $prompt)

    FIND:

    var input = document.querySelector("#textinput-prompt");
    REPLACE WITH:

    var input = document.querySelector("#prompt");
    As a tip, most browsers ship with developer tools these days, so using those to inspect live elements, to see what their attributes are, may prove beneficial to you in the future.


    flipLyric wrote:

    This gives focus to the text field every time one is present when a page loads (which is what I'll want for this game) but frustratingly, the focus disappears soon after!  I've tried a couple of other Javascript methods of giving focus to an element within this file but still couldn't get them to work.  Not really sure what else I should try at this point.


    Unfortunately, the autofocus attribute is not yet pervasively supported across browsers, so it's a little premature to try to depend on it (it's not in IE <10, nor is it in iOS at all; according to caniuse.com: autofocus).
  • InsertCoin25 wrote:

    Is there any news related to a new release of Sugarcube and, if so what features are going to be added?


    This close to the holidays here about, I haven't really been working on SugarCube much.
  • Sorry to derail this thread but you've been so kind in helping already I thought I'd give this another shot...

    I did try and inspect the element before - as far as I can tell, it came up with the name "input#prompt".  I've tried both #prompt and input#prompt in the code you supplied and still couldn't get it to work.  I'm sure I've done something stupid!

    I thought I'd post the .tws file in case anyone can help out with this.  I tried exporting the source but that caused an error and didn't complete.

    As I said, I'm using 1.3.5 solely because that's the only version with the textinput macro working in the way I need it to for the kind of fake command line interface I want to use in the game.  Perhaps if there's a more sane way of doing this in a more current version that someone could kindly suggest, I can try that instead of this?  I really want this particular effect - I'm sure there are arguments against using Twine for it, given that what I'm doing effectively breaks the input paradigm, but Twine's back-end is perfect for this sort of game and there will be points in the game where I want to use a conventional Twine interface as well.

    Here's the 1.3.5. Sugarcane header file with the experimental text macro:

    https://drive.google.com/file/d/0B9UbOgQWqQg3UW9RdWlobGZiQkE/view?usp=sharing

    Here's the .tws file:

    https://drive.google.com/file/d/0B9UbOgQWqQg3Rm1WTVZhSGpfNWM/view?usp=sharing

    Any help greatly appreciated!
  • The "typewriter" code is causing timing issues, so the focus code will have to go within it.  Completely remove the focus code I gave you previously and replace your current "typewriter" code with this:

    (function(){var render2=Passage.prototype.render;Passage.prototype.render=function(){var b=render2.apply(this);if(this.tags){var r=new RegExp("t8n.typewriter.([0-9]+)(?:[^0-9]|$)","g");var t=r.exec(this.tags.toString());if(t){typeout(b,0+t[1])}else{setTimeout(function(){var el=document.querySelector('input[type="text"]');if(el!==null){el.focus()}},1100)}}return b};var typeout=function(c,t){var Furl=function(current){this.n=current;this.out=false;this.data=current.nodeValue;current.nodeValue="";this.kids=[];var cn=current.childNodes;if(current.style&amp;&amp;current.style.display==="none"){return}while(cn.length>0){var f=new Furl(cn[0]);current.removeChild(cn[0]);f.out=true;this.kids.push(f)}};var nodes=new Furl(c);var unfurl=function(furled,d){var n=furled.n;if(furled.out){d.appendChild(n);furled.out=false}if(furled.data){n.nodeValue+=furled.data[0];furled.data=furled.data.slice(1);return true}for(var j=0;j<furled.kids.length;j++){var ret=unfurl(furled.kids[j],n);if(ret){return true}}return false};var title=state.history[0].passage.title;var intr=setInterval(function(){if(state.history[0].passage.title===title&amp;&amp;unfurl(nodes,null)){return}clearInterval(intr);setTimeout(function(){var el=document.querySelector('input[type="text"]');if(el!==null){el.focus()}},550)},t)}})();
    The focus code in that will attempt to focus the first &lt;input type=&quot;text&quot;&gt; element within the incoming passage, so it should work on all passages without tinkering.  The focus code runs after the "typewriter" effect, and I've set the focus timeout to a fairly long duration (500 ms), so there shouldn't be any issues now.


    As a separate issue.  The "typewriter" interval delay given as part of the passage tags is in whole milliseconds, so you cannot specify fractional delays.  Additionally, the "typewriter" does not handle fractional delays anyway (t8n-typewriter-0.8 is actually parsed as t8n-typewriter-0).  Beyond that, browsers enforce minimum delays anyway (generally in the range of 410 ms).


    You also have a syntax error at the end of your stylesheet passage: (you've inserted styles within the closing curly-brace of .tab)

    .tab {
    text-indent:40px;
    margin:0px;

    .revision-span-in {
    opacity: 0;
    }
    .revision-span:not(.revision-span-out) {
    transition: 0s; -webkit-transition: 0s;
    }
    .revision-span-out {
    position:absolute;
    opacity: 0;
    }

    }
  • Thanks so much for taking the time to do this - it's really amazing that you're helping to this extent.  Please feel free to give up at any point and I'll find alternative methods!

    I removed the "focus" passage and replaced the typewriter script with your code as per your instructions, but that gives "typewriter: Unexpected token ILLEGAL" - any thoughts?  I have triple-checked that I pasted it correctly.
  • I tested it, so I know it works.  Maybe something got screwed up in translation.  Try this attachment (there are no line breaks, so it's one long line; about 1283 bytes worth).
  • Still no joy - no idea what's happening - sorry.  Maybe I could try your .tws file instead?
  • I didn't use a TWS.  The reason being is that I don't have Twine 1.3.x installed anymore, and Twine 1.4.1+ isn't compatible with your version of Sugarcane so I can't use it as-is with my installed version (1.4.2).  So, for testing, I converted your TWS file into TWEE format and used that with an old version of tweego that is compatible with your version of Sugarcane.  I suppose I could try importing that TWEE file back into a TWS, however, I'm unsure if it would be compatible with Twine 1.3.x (again, since I'm using 1.4.2).

    You're really not doing yourself any favors by using the old and moldy (it's hard for anyone to help you since no one is using it anymore).  You'd be better off simply using your macros in Twine 1.4.2 and its version of Sugarcane.  You'd be able to ditch some of what you're trying to use now, like the elseif script, since that functionality in built into newer versions, and replacing the built-in &lt;&lt;textinput&gt;&gt; with your version, or a version that does what you want, shouldn't be an issue.  /shrug
  • Yeah, it would definitely make sense to switch.  I tried the latest version yesterday and I think I'll probably do what you said and start again in that.  I think I may well try and do this game with more traditional twine functionality as this stuff is just holding me back at the moment.

    It is a real shame that "submit value to text box on pressing enter" was removed after 1.3.5. though - that's such a nice feature wherever text boxes are employed.  Seems like Twine in general has a bit of a weird relationship with text input so it's understandable!

    Many thanks for your help with this - it's really great to find a community of people who are willing to help out someone who is just starting / isn't able to do any of the real code side themselves.  Hugely encouraging.
  • Updated everything to 1.4 with your new typewriter script...and it worked!  :) :)

    Absolutely fantastic - thank you so much!  I'm really excited to work on this further now I've got everything displaying just how I want.

    Just in case anyone else is misguided enough to do this, here's the script for the textinput macro from 1.3.5. with the oh-so-evil "advance to the desired passage on pressing enter" functionality:
    macros.textinput={handler:function(a,b,c){if(c[1]){var d=c[0].replace(/\$/g,"");el=document.createElement("span");var e="<input id='"+d+"' type='text' onkeypress=\"var charCode; if(event &amp;&amp; event.which){charCode = event.which;}else if(window.event){event = window.event;charCode = event.keyCode;}if(charCode == 13) {state.history[0].variables."+d+" = this.value; state.display('"+c[1]+"')}\"/>";if(c[2]){e+="<input type='button' value='"+c[2]+"' onclick=\"state.history[0].variables."+d+" = getElementById('"+d+"').value; state.display('"+c[1]+"');\"/>"}el.innerHTML=e;a.appendChild(el)}else{throwError(a,'"[PassageName]" parameter missing')}}};
  • I'm glad you got everything working to your satisfaction. :)


    flipLyric wrote:

    Just in case anyone else is misguided enough to do this, here's the script for the textinput macro from 1.3.5. with the oh-so-evil "advance to the desired passage on pressing enter" functionality:


    Well, let's not be hyperbolic.  I don't think anyone considers the action-on-enter behavior "evil" (case in point, SugarCube's &lt;&lt;textbox&gt;&gt; macro optionally exhibits that very behavior).  I'm unsure why it was removed from the vanilla story format's &lt;&lt;textinput&gt;&gt; macro, but I doubt the reason was because it was considered "evil" (it was probably just an oversight).
  • Based on the comment associated with the patch (dated 26-dec-2013) which changed/re-implemented the textinput macro it would seem that the Enter Key Trigger removal was meant to be reviewed at some point and that maybe that review never happened.
    [quote]
    The previous <<textinput>> behaviour of triggering the passage change via the Enter key has been removed for now.
  • Hah, please forgive my hyperbole!

    I actually completely understand the reasoning for removing it: that it's not in keeping with the normal navigation paradigms of Twine.  Can't find it now but I remember reading a quote from a developer to that effect.
  • Instead of removing the Enter Key feature completely, a better option would of been to make it optional. That would of allowed the story author the choice to use the feature if the wished, like the texbox macro within SugarCube.
Sign In or Register to comment.