Jump to content

How to set field focus in ready.php


Recommended Posts

Hi All,

I'm using ready.php to add some values to new pages, so that after I type in the title of the page and hit save, when I get to the edit page of the full set of fields, certain fields have pre-filled values. The routine is working well, but I want to add one more item, to wit:

- I want to set the field focus (i.e. the cursor is in that field) to a field called "list_price." Can that be done, in ready.php, and if so, what's the best method?

Here's the code I have already:

$pages->addHook('added', function(HookEvent $event) {
      $page = $event->arguments(0);

      if ( $page->template == 'book_sales' )
            {
            #.............................................

            $parent      = $page->parent();
            $parent_path = $parent->path();
            $last_item_number_selectors = "parent=$parent_path," .
                                          "sort=-item_number,"   .
                                          "$skiptrash,"          .
                                          "limit=1";

            $last_item_number = wire("pages")->get("$last_item_number_selectors")->item_number;

			# the item_number has a 2 letter prefix, followed by 4 digits that have leading zeroes,
			# and the number has to be incremented, while retaining the leading zeroes.

            $part1 = substr($last_item_number,0,2);
            $part2 = substr($last_item_number,2);

            $part2inc = intval($part2) + 1;
            $part2new = sprintf('%04d', $part2inc);

            $new_item_number   = $part1 . $part2new;
            $page->item_number = $new_item_number;

            #.............................................

            $admin_now = time();

            $page->list_date = wire(datetime)->date($format='Y-m-d', $admin_now);

            $page->list_year = wire(datetime)->date($format='Y', $admin_now);

            #.............................................

            $page->of(false);
            $page->save();
            $page->of(true);
            }
});

Thanks!

Peter

 

Link to comment
Share on other sites

1 hour ago, Peter Falkenberg Brown said:

I want to set the field focus (i.e. the cursor is in that field) to a field called "list_price." Can that be done, in ready.php, and if so, what's the best method?

No, you cannot focus an input using PHP. Autofocus is usually achieved using JavaScript. E.g.

// get input you want to focus
// in this case we know its ID, so we are grabbing it that way
// we focus it using the focus() method
// you could throw this into a function if you wanted
document.getElementById("your_input_id").focus();

Alternatively, you can do this in the HTML, i.e. the PHP code that is outputting the HTML by using the autofocus attribute.

<label for="my_input">Book Title:</label>
<!-- the browser will focus this input when the page loads -->
<input type="text" id="my_input" name="my_input" autofocus>
<label for="my_input">First name:</label>
<!-- the browser will focus this input when the page loads -->
<input type="text" id="my_input" name="my_input" autofocus>

References

https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus

https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus

https://www.w3schools.com/tags/att_input_autofocus.asp

Read about the accessibility considerations if you will be using autofocus.

Link to comment
Share on other sites

Thanks, @kongondo...

In the PW admin, when you click on "New" to add a record, the title field has focus, with the cursor in it.

However, when you then hit save and get to the edit page, none of the fields have focus.

These pages are part of the built in admin, so I'm wondering if they can be tweaked without editing the core files.

Based on your description, it would seem that the New record page must have some type of focus mechanism, either with Javascript or HTML.

What are your thoughts?

Thanks!

Peter

 

Link to comment
Share on other sites

Just a thought. Could you use $config->js() or $config->jsConfig() to set the field name having the focus and then have the js in admin.js defaulting to title if required?

Link to comment
Share on other sites

1 hour ago, dotnetic said:

There is also a possibility to do a redirect and focus the field via PHP but I don't remember the query parameter. Maybe @bernhard can jump in for the correct syntax.

you can add ?field=foo or ?fields=foo,bar but that will then show only the fields listet in the url and I think it does not change the focus.

Should this field always get the focus? Then I'd probably hook Inputfield::render and add the js there.

Link to comment
Share on other sites

35 minutes ago, Peter Falkenberg Brown said:

it should always get the focus when it goes from adding a new record to the edit page of the new record.

What field is this? Title field?

There's two steps to this:

  1. Add your custom JS to the admin
  2. Add your code to the custom JS file.

 

For #1, there's many different ways to do this. Have a look at these two threads, for instance:

For #2, it depends on the input you want to target. The easiest way is to target it by ID. Or grab it using ProcessWire Inputfields API and focus it. If you can be a bit more specific about the field, we can provide ready-to-go code (some of which have been posted here already, actually).

Link to comment
Share on other sites

OK.

STEP #1

Create a file, e.g admin.js and save it to your preferred location. In this example, we have saved it at site/templates/scripts/admin/admin.js

STEP #2

Add either of the below JS code in admin.js (you don't need both; choose one or the other).

jQuery version:

$(document).ready(function () {
  // #wrap_Inputfield_list_price is the ID ProcessWire will assign to your list_price field's wrapper (an li element).
  // you can confirm this by examining the markup around your 'list_price' input in dev tools.
  // Inputfields is a utility files that ships with ProcessWire.
  // more details: inputfields.js - JS specific to behavior of ProcessWire Inputfield forms.
	Inputfields.focus("#wrap_Inputfield_list_price")
})

Plain JavaScript version:

const InputfieldAdminScripts = {
	focusAnInputfield: function (inputfield_id) {
		const inputfieldToFocus = document.getElementById(inputfield_id)
		// if we found the element,focus it
		if (inputfieldToFocus) {
			inputfieldToFocus.focus()
		}
	},
}

// DOM ready
document.addEventListener("DOMContentLoaded", function () {  	
	InputfieldAdminScripts.focusAnInputfield("Inputfield_list_price")
  	// this will also work here. you can use it instead of the above
	// Inputfields.focus("#wrap_Inputfield_list_price")
})

STEP #3

Inside ready.php, add this code to load admin.js. You can modify this to only load under certain conditions if you wish.

<?php namespace ProcessWire;

$this->addHookBefore('AdminTheme::getExtraMarkup', function (HookEvent $event) {
  $config = $this->wire->config;
  $url = $config->urls->templates;
  $config->scripts->add($url . "scripts/admin/admin.js");
});

 

Please note that in Chrome (and maybe FF as well), if you have your dev tools opened and the cursor currently focused on it, the inputfield might not seem to be focused, but it actually is.

 

😀

 

Edited by kongondo
  • Like 4
Link to comment
Share on other sites

Hi @kongondo,

This is so cool. Thank you VERY much. It works perfectly.

I'm wondering what the best way to load it under certain conditions would be. Right now, it places the cursor in the field whenever I go into edit mode, not just on the initial edit that comes right after adding a record. In my ready.php file I have this:

$this->addHookBefore('AdminTheme::getExtraMarkup', function (HookEvent $event) {
  $config = $this->wire->config;
  $url = $config->urls->templates;
  $config->scripts->add($url . "scripts/admin.js");
});

$pages->addHook('added', function(HookEvent $event) {
      $page = $event->arguments(0);

      if ( $page->template == 'book_sales' )
            {
			.... etc....

So, in the code that comes after the addHook('added') it only operates in the subsequent edit screen, but not when I go back to a record and click on edit.

(At least I don't think so.)

Is there a way to only initiate the cursor focus during that initial edit?

Thank you again!

Peter

 

  • Like 1
Link to comment
Share on other sites

2 hours ago, Peter Falkenberg Brown said:

Is there a way to only initiate the cursor focus during that initial edit?

Below is one way to detect if the page is new, in which case add our script. The disadvantage of this is that the whole file is only included under certain conditions as opposed to a just a function inside the file being called under certain conditions. This means the admin.js file will only be used for focusing the list_price field. There are other ways around this. Hopefully others will chime in with better solutions/ideas.

Change the ready.php code I suggested above to this one:

<?php namespace ProcessWire;
$this->addHookBefore('ProcessPageEdit::buildForm', function (HookEvent $event) {
  $page = $event->object->getPage();
  // new pages GET a 'new'  query string/parameter, i.e. &new=1
  // we check that
  $new = (int) $this->wire('input')->get('new');
  if ($new && $page->template == 'book_sales') {
    $config = $this->wire->config;
    $url = $config->urls->templates;
    $config->scripts->add($url . "scripts/admin/admin.js");
  }
});

There's probably other more elegant ways to detect if a page is new but I cannot remember any of the top of my head at the moment.

  • Like 1
Link to comment
Share on other sites

On 10/3/2021 at 9:12 AM, Peter Falkenberg Brown said:

Based on your description, it would seem that the New record page must have some type of focus mechanism, either with Javascript or HTML.

ProcessPageAdd.module has this (that is the process module that is executed when adding a page):

$form->addClass('InputfieldFormFocusFirst');

And inputfields.js has this note:

 * - InputfieldFormFocusFirst: Class assigned to form that should focus the first field. 

ProcessPageEdit does not add this class, so there is no field focus on edit screens.

What I'd do is probably this:

// in site/ready.php
$wire->addHookAfter("Inputfield(name=title)::render", function(HookEvent $event) {
  // we focus the field only if it is empty
  if($event->object->value) return;

  // show a hint and focus the field
  $event->return .= "<div>Please enter something here</div>"
    ."<script>$(function() { Inputfields.focus('#Inputfield_title') })</script>";
});

 

  • Like 3
Link to comment
Share on other sites

Guys, I found the method that I meant. It is not the field=fieldname URL that Bernhard mentioned.

A long time ago I also tried to tackle a similar task, where I wanted to remember and focus the last edited field after you saved a page.

I opened a github issue for this and later Ryan added some methods that he mentioned in a reply, that achieve the behaviour I and @Peter Falkenberg Brown want.

Just add a #focus-field_name to the URL when the page is saved and the redirect is executed and the field gets focused.

An example URL looks like https://domain.com/processwire/page/edit/?id=1056#focus-subheadline and would focus the field named "subheadline".

I am a fan of using native methods that ProcessWire offers, over custom ones. But what the others wrote is also viable.

I will provide a hook, just have to test it out, but it goes somewhat like this:

<?php namespace ProcessWire;
$this->addHookBefore('ProcessPageEdit::processSaveRedirect', function (HookEvent $event) {
 	 $url = $event->arguments(0);
    $page = $event->object->getPage();
    if ($page->template == "book_sales" && !$page->isTrash()) {
      if ($url !== "../") {
        $goto =$page->url . '#focus-list_price";
        $event->arguments = array($goto);
      }    
}

 

  • Like 3
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...