admin管理员组

文章数量:1279209

I am adding in a new item into a repeat. I want to focus in on the first input field of the newly inserted entry. If I use 'setfocus' with the 'control' set to the repeat id itself - nothing happens. If I instead set the 'id' of my entry to 'fname' (in this example) and then call 'setfocus' with control set to 'fname' - the focus is shifted to the first item in the repeat.

I wasn't able to use AVTs to generate a unique ID attribute from within the repeat (the input isn't ever shown if I do that).

Any advice on how to achieve this?

<?xml-stylesheet href="xsltforms/xsltforms.xsl" type="text/xsl"?>
<html
        xmlns=";
        xmlns:xs=";
        xmlns:xf=";
        xmlns:ev="; lang="en">
<head>
    <xf:model id="model">
        <xf:instance id="data">
            <data xmlns="">
                <rows>
                    <row>
                        <fname>joe</fname>
                        <lname>bloggs</lname>
                    </row>
                </rows>
            </data>
        </xf:instance>
    </xf:model>
</head>
<body>
    <h1> Focus Test </h1>
    <xf:output value="index('r1')"/>
    <xf:repeat id="r1" ref="instance('data')/rows/row">
        <xf:input id="fname" ref="fname"/>
        <xf:input ref="lname"/>
    </xf:repeat>
    <xf:trigger>
        <xf:label>+</xf:label>
        <xf:action ev:event="DOMActivate">
            <xf:insert origin="instance('data')/rows/row[last()]" ref="instance('data')/rows/row"/>
            <xf:setvalue ref="instance('data')/rows/row[last()]/fname"/>
            <xf:setvalue ref="instance('data')/rows/row[last()]/lname"/>
            <xf:setfocus control="fname"/>
        </xf:action>
    </xf:trigger>
</body>
</html>

I am adding in a new item into a repeat. I want to focus in on the first input field of the newly inserted entry. If I use 'setfocus' with the 'control' set to the repeat id itself - nothing happens. If I instead set the 'id' of my entry to 'fname' (in this example) and then call 'setfocus' with control set to 'fname' - the focus is shifted to the first item in the repeat.

I wasn't able to use AVTs to generate a unique ID attribute from within the repeat (the input isn't ever shown if I do that).

Any advice on how to achieve this?

<?xml-stylesheet href="xsltforms/xsltforms.xsl" type="text/xsl"?>
<html
        xmlns="http://www.w3./1999/xhtml"
        xmlns:xs="http://www.w3./2001/XMLSchema"
        xmlns:xf="http://www.w3./2002/xforms"
        xmlns:ev="http://www.w3./2001/xml-events" lang="en">
<head>
    <xf:model id="model">
        <xf:instance id="data">
            <data xmlns="">
                <rows>
                    <row>
                        <fname>joe</fname>
                        <lname>bloggs</lname>
                    </row>
                </rows>
            </data>
        </xf:instance>
    </xf:model>
</head>
<body>
    <h1> Focus Test </h1>
    <xf:output value="index('r1')"/>
    <xf:repeat id="r1" ref="instance('data')/rows/row">
        <xf:input id="fname" ref="fname"/>
        <xf:input ref="lname"/>
    </xf:repeat>
    <xf:trigger>
        <xf:label>+</xf:label>
        <xf:action ev:event="DOMActivate">
            <xf:insert origin="instance('data')/rows/row[last()]" ref="instance('data')/rows/row"/>
            <xf:setvalue ref="instance('data')/rows/row[last()]/fname"/>
            <xf:setvalue ref="instance('data')/rows/row[last()]/lname"/>
            <xf:setfocus control="fname"/>
        </xf:action>
    </xf:trigger>
</body>
</html>
Share Improve this question edited Feb 23 at 21:59 jonrsharpe 122k30 gold badges267 silver badges474 bronze badges asked Feb 23 at 21:53 monojohnnymonojohnny 6,19116 gold badges64 silver badges86 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

The XForms specification says that

the current index of the repeat determines the set of run-time objects that contains the desired target object.

The "set of run-time objects" is the set of controls in one row of your table. So if the index (which is shown above your table) is 2, the xf:setfocus should target the control corresponding to fname in the second row.

Apparently XSLTForms does not implement it that way. After the xf:insert and xf:setvalue actions, the index becomes 2, but xf:setfocus always targets the control in the first row and also resets the index to 1. Moreover, for the target to be chosen from the second row, the implementation of the xf:setfocus action would have to run after that row is created, that is, wait until after the xforms-refresh event. But it runs much earlier.

A workaround is as follows: Do not execute an xf:setfocus function (which dispatches a synchronous xforms-focus event on the target control). Instead, dispatch a custom event setfocus asynchronously (with delay="1") so that it runs after xforms-refresh:

<xf:trigger>
  <xf:label>+</xf:label>
  <xf:action ev:event="DOMActivate">
    <xf:insert origin="instance('data')/rows/row[last()]"
      ref="instance('data')/rows/row"/>
    <xf:setvalue ref="instance('data')/rows/row[last()]/fname"/>
    <xf:setvalue ref="instance('data')/rows/row[last()]/lname"/>
    <xf:dispatch name="setfocus" targetid="fname" delay="1"/>
  </xf:action>
</xf:trigger>

And include the definition of this event and its Javascript handler in a <script> element in the <head>:

XsltForms_xmlevents.define("setfocus", true, true, function(event) {
  let repeat = event.target;
  while (!repeat.classList.contains("xforms-repeat"))
    repeat = repeat.parentElement;
  XsltForms_xmlevents.dispatch(
    repeat.querySelectorAll(".xforms-repeat-item")
      .item(repeat.xfElement.index - 1)
      .querySelector(`[oldid=${event.target.id}]`),
    "xforms-focus");
});

This implementation

  • moves up from the event target to the <div class="xforms-repeat"> that wraps the entire repeat
  • looks up the repeat's current index (repeat.xfElement.index)
  • selects from the row (<div class="xforms-repeat-item">) with that index the control that corresponds to the event target ID. The correspondence is maintained by XSLTForms through an oldid attribute in the controls in all rows (<span oldid="fname"> in this example).
  • An xforms-focus event is then dispatched on the selected control.

This makes the simplifying assumption that the event target is only contained in one repeat. The general case of nested repeats must look up the current index of each of them.

Based on the post from Heiko Theißen.
For this particular case at least - I didn't need to introduce a Javascript function - I just added the following action in my model section:

<xf:model id="model">
...
<xf:action ev:event="setfocus">
    <xf:setfocus control="fname"/>
 </xf:action>
...
</xf:model>

And dispatched to the custom event with a delay as suggested.

<xf:action ev:event="DOMActivate">
            <xf:insert origin="instance('data')/rows/row[last()]" ref="instance('data')/rows/row"/>
            <xf:setvalue ref="instance('data')/rows/row[last()]/fname"/>
            <xf:setvalue ref="instance('data')/rows/row[last()]/lname"/>
            <xf:dispatch name="setfocus" targetid="model" delay="1"/>
</xf:action>

本文标签: xformsHow to set focus to an input within a repeatStack Overflow