Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hwo can I set HTML select element value? #613

Closed
thujikun opened this issue Aug 30, 2017 · 15 comments
Closed

Hwo can I set HTML select element value? #613

thujikun opened this issue Aug 30, 2017 · 15 comments

Comments

@thujikun
Copy link

I tried following code. But I cannot set the value.

await page.click('.select-element[name=select]');
await page.click('.select-element[name=select] > option[value="123"]');

And I set the value with page.evaluate.
But I need to virtual DOM value.
So following code is not useful for me.

    async setSelectVal(sel, val) {
        await page.evaluate((data) => {
            return document.querySelector(data.sel).value = data.val;
        }, {sel, val});
    }
@aslushnikov
Copy link
Contributor

@thujikun I don't follow, why the second approach with page.evaluate doesn't work for you?

@thujikun
Copy link
Author

@aslushnikov I 'm using React. And I think page.evaluate is same with setting value with JavaScript. So it doesn't change React's virtual DOM value. And I think the method to set select value like click method should be because it's normal user's action. The user doesn't use page.evaluate

@sean-hill
Copy link

Also a lot of websites use jQuery 🤒 And have code like $(selector).on('change', () => {}) how could we trigger a change event after we set the value of a select box?

@Garbee
Copy link
Contributor

Garbee commented Sep 1, 2017

@sean-hill By firing the change event manually.

As far as React goes. It seems like your component would need to expose an API to programatically change the value. Just like .value='whatever' is doing. So look for one of those since they should exist.

Could someone provide a react-based sample test though? That would help loads in understanding the exact situation and how to resolve it.

@nabati
Copy link

nabati commented Sep 5, 2017

Doesn't this

    async setSelectVal(sel, val) {
        await page.evaluate((data) => {
            return document.querySelector(data.sel).value = data.val;
        }, {sel, val});
    }

actually just circumvent the underlying issue, that you can't click on certain elements? Will there be support for this in the future? I think this circumvention is more of an escape hatch than it should be a default approach.

@fixingcredit
Copy link

@thujikun I had to deal with the same issue and this seems to work for me. A bit of a hack though:

await page.evaluate(() => {
    document.querySelector('#element > option:nth-child(3)').selected = true;
    element = document.querySelector('#element');
    var event = new Event('change', { bubbles: true });
    event.simulated=true;
    element.dispatchEvent(event);
});

@thujikun
Copy link
Author

thujikun commented Sep 19, 2017

@fixingcredit
Thx! I succeeded changing value managed in Redux by your hack. But I hope Puppeteer supports the feature to set the value to select element.

@aslushnikov
Copy link
Contributor

aslushnikov commented Sep 25, 2017

@thujikun the Page.select method has just landed and should address this issue.

@thujikun
Copy link
Author

@aslushnikov
Awesome! This is the method I wanted!

@DaveRandom
Copy link

DaveRandom commented Sep 27, 2017

Incomplete polyfill for Page.select() based on @fixingcredit's code. Should cover the most common case of setting the value to an option that is known to exist in a dropdown. Does not handle multiple selects and does not handle errors gracefully when the selector matches nothing or the specified value doesn't exist in the options.

if (page.select === undefined) {
    page.select = async function(selector, value) {
        await page.evaluate((selector, value) => {
            let element = document.querySelector(selector);
            element.querySelector('option[value="' + value + '"]').selected = true;

            let event = new Event('change', { bubbles: true });
            event.simulated = true;

            element.dispatchEvent(event);
        }, selector, value);
    };
 }

@thujikun
Copy link
Author

@aslushnikov Doesn't this select method trigger change event on selecting value?

@aslushnikov
Copy link
Contributor

@thujikun yes it does

@AlexChung1995
Copy link
Contributor

AlexChung1995 commented Oct 18, 2017

Can I suggest adding something to the page.select function?
I was thinking the function should throw an error if the option doesn't exist. In my use case, it was important to know if the select was executed. I think this would be the case for many use cases.

The code I added was:
if (!options.some(function (v) {return values.indexOf(v.value) >= 0;})){ throw new Error('Option does not exist'); }

This is the altered select function:
`async select(selector, ...values) {
await this.$eval(selector, (element, values) => {
if (element.nodeName.toLowerCase() !== 'select')
throw new Error('Element is not a element.'); const options = Array.from(element.options); if (!options.some(function (v) {return values.indexOf(v.value) >= 0;})){ throw new Error('Option does not exist'); } if (element.multiple) { for (const option of options) option.selected = values.includes(option.value); } else { element.value = values.shift(); } element.dispatchEvent(new Event('input', { 'bubbles': true })); element.dispatchEvent(new Event('change', { 'bubbles': true })); }, values); }`

@aslushnikov
Copy link
Contributor

@AlexChung1995 feel free to send a PR or file a new issue, we'll discuss it there

@dsheiko
Copy link
Contributor

dsheiko commented Aug 9, 2018

I would extend also ElementHandle:

/**
 * Set value on a select element
 * @param {string} value
 * @returns {Promise<Undefined>}
 */
ElementHandle.prototype.select = async function( value ) {
  await this._page.evaluateHandle( ( el, value ) => {
      const event = new Event( "change", { bubbles: true });
      event.simulated = true;
      el.querySelector( `option[value="${ value }"]` ).selected = true;
      el.dispatchEvent( event );
  }, this, value );
};

Then one can use it like:

const combobox= await page.$('select');
await combobox.select("optionFoo");

leeroybrun added a commit to leeroybrun/magepack that referenced this issue Sep 5, 2021
When selecting the product's options (for configurable products with no swatches), we should then dispatch a "change" and "input" events.
This is implemented here in Puppeteer : https://github.com/puppeteer/puppeteer/blob/dd470c7a226a8422a938a7b0fffa58ffc6b78512/src/common/JSHandle.ts#L597
You can also find more info here : puppeteer/puppeteer#613 (comment)
krzksz pushed a commit to magesuite/magepack that referenced this issue Sep 6, 2021
When selecting the product's options (for configurable products with no swatches), we should then dispatch a "change" and "input" events.
This is implemented here in Puppeteer : https://github.com/puppeteer/puppeteer/blob/dd470c7a226a8422a938a7b0fffa58ffc6b78512/src/common/JSHandle.ts#L597
You can also find more info here : puppeteer/puppeteer#613 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants