microsoft / playwright

Playwright is a framework for Web Testing and Automation. It allows testing Chromium, Firefox and WebKit with a single API.
https://playwright.dev
Apache License 2.0
66.79k stars 3.66k forks source link

[Question] is there a way to select text in the DOM? #5714

Closed amit-parizian-erudite closed 3 years ago

amit-parizian-erudite commented 3 years ago

Hi! is there a way to emulate selection of some text in a paragraph? for example, if the page contains something like: <p> here I have some text that I want to select <p> and I want to select a part of it, say "have some". is there a way to do it? by selecting I mean like a user can do with the mouse: image

amit-parizian-erudite commented 3 years ago

BTW, I tried to do something like this:

const elem = await this.page.$("text=have some");

const box = await elem.boundingBox();

await this.page.mouse.move(box.x, box.y);

await this.page.mouse.down();

await this.page.mouse.move(box.x + 30, box.y + 1);

await this.page.mouse.up();

but box.x and box.y is the top left of the box so I can only select words from the start of the paragraph node (of course I can play around with the numbers, but it is not really a solution). so I am looking for some native way to do this, preferably by the words that I want to select and not coordinates. Thanks in advance!

pavelfeldman commented 3 years ago

You can do this with DOM api, but not via the mouse drag atm:

await pagea.$eval('selector', element =>
    const range = element.ownerDocument.createRange();
    range.setStart(element, 10);
    range.setEnd(element, 20);
    const selection = element.ownerDocument.defaultView.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);
})

Is selecting using drag essential to your use case?

amit-parizian-erudite commented 3 years ago

thanks a lot for the quick answer! no it's not essential. bu the solution you gave me doesn't work (or I don't fully understand how to use it). say I have <p> this is some text that I have <strong> and this is some bold text I have </strong> </p> so if I use createRange on the

element it returns "this is some text that I have" for range (0,1) and "and this is some bold text I have" for range (1,2). and for anything other than that it says "There is no child at offset X." if I am doing something wrong I'd be happy to get a link to some guide to this solution. if not, do you have any other ideas on how can I select certain words inside the paragraph? thanks.

yury-s commented 3 years ago

The solution above is slightly incorrect as you need to specify positions within text nodes, so what you need is something like this:

  await page.setContent(`<p> this is some text that I have <strong> and this is some bold text I have </strong> </p>`);
  await page.$eval('p', element => {
    const range1 = element.ownerDocument.createRange();
    const text1 = element.firstChild;
    const text2 = element.firstElementChild.firstChild;
    range1.setStart(text1, 10);
    range1.setEnd(text2, 20);
    const selection = element.ownerDocument.defaultView.getSelection();
    selection.removeAllRanges();
    selection.addRange(range1);
  });

It will select a range which spans across several DOM nodes.

image

Please reopen this issue if it doesn't work for you.

amit-parizian-erudite commented 3 years ago

unfortunately this didn't work. using this method selects nothing for me for some reason (don't know why). also, I found out that it's better to emulate the mouse since this is how the users will select the text. as i've said earlier, it works with the mouse but the only thing I could think of was using the box. is there any way to use the mouse to select text by the content? ideally, something like page.mouse.move(text=middle of line) thanks a lot for the help and the fast responses

yury-s commented 3 years ago

unfortunately this didn't work. using this method selects nothing for me for some reason (don't know why).

Do you mean this particular example didn't work or it didn't work for your DOM tree? If the latter, could you share source of the page where you try to select the text and the code that does it, otherwise it's hard to give a meaningful advise?

is there any way to use the mouse to select text by the content? ideally, something like page.mouse.move(text=middle of line)

There is no such API.

amit-parizian-erudite commented 3 years ago

<div class="v-expansion-panel-content article-content" fragment="39e34d5db0"><div class="v-expansion-panel-content__wrap"><div id="b7b6f412-d60d-31b0-c257-b306b7f00041" class="dite_container"><!----><div data-test-id="article-content" class="article-content"><div class="secondary--text content"><div class="editor"><div class="menububble" style="left: 128px; bottom: 578px;"><!----><div class="v-menu"><!----></div><button dark="" role="button" aria-haspopup="true" aria-expanded="false" class="menu__item"><img src="/img/dictionary.5482c896.svg"></button><!----><div class="d-flex justify-content-center"><!----><!----><!----></div><!----><!----><hr role="separator" aria-orientation="vertical" class="menububble__divider v-divider v-divider--inset v-divider--vertical theme--light"><div data-v-1eabfd10="" class="menu__item color-picker menu__item"><button data-v-1eabfd10="" dark="" data-test-id="highlight-color-picker-button" role="button" aria-haspopup="true" aria-expanded="false" class="menu__content__color-picker-btn color-picker__main-btn d-flex align-center"><div data-v-1eabfd10="" class="menu__content__color-picker" style="background-color: inherit;"><img src="/img/highlight.60e1b5b3.svg" width="24" height="24"></div><span data-v-1eabfd10="" aria-hidden="true" class="v-icon notranslate v-icon--svg theme--light" style="font-size: 20px; height: 20px; width: 20px; color: rgb(0, 0, 0); caret-color: rgb(0, 0, 0);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="20px" width="20px" role="img" aria-hidden="true"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"></path></svg></span></button><div data-v-1eabfd10="" class="v-menu"><!----></div><button data-v-1eabfd10="" role="button" aria-haspopup="true" aria-expanded="false" class="color-picker__main-btn"><!----></button></div></div><div class="editor__content-wrapper"><div class="editor__content tiptap-content"><div class="ProseMirror" contenteditable="false" tabindex="0"><p class=""><strong>Bloom's taxonomy is a set of three hierarchical models used to classify educational learning objectives into levels of complexity and specificity.</strong> <strong>The three lists cover the learning objectives in cognitive, affective and sensory domains.</strong> The cognitive domain list has been the primary focus of most traditional education and is frequently used to structure curriculum learning objectives, assessments and activities. The models were named after Benjamin Bloom, who chaired the committee of educators that devised the taxonomy. He also edited the first volume of the standard text, Taxonomy of Educational Objectives: The Classification of Educational Goals.</p></div></div></div></div></div><!----></div><br></div><div id="574e3162-1a5a-3031-e6af-108b123553d0" class="dite_container"><!----><div data-test-id="article-content" class="article-content"><div class="secondary--text content"><div class="editor"><div class="menububble" style="left: 0px; bottom: 0px;"><!----><div class="v-menu"><!----></div><button dark="" role="button" aria-haspopup="true" aria-expanded="false" class="menu__item"><img src="/img/dictionary.5482c896.svg"></button><!----><div class="d-flex justify-content-center"><!----><!----><!----></div><!----><!----><hr role="separator" aria-orientation="vertical" class="menububble__divider v-divider v-divider--inset v-divider--vertical theme--light"><div data-v-1eabfd10="" class="menu__item color-picker menu__item"><button data-v-1eabfd10="" dark="" data-test-id="highlight-color-picker-button" role="button" aria-haspopup="true" aria-expanded="false" class="menu__content__color-picker-btn color-picker__main-btn d-flex align-center"><div data-v-1eabfd10="" class="menu__content__color-picker" style="background-color: inherit;"><img src="/img/highlight.60e1b5b3.svg" width="24" height="24"></div><span data-v-1eabfd10="" aria-hidden="true" class="v-icon notranslate v-icon--svg theme--light" style="font-size: 20px; height: 20px; width: 20px; color: rgb(0, 0, 0); caret-color: rgb(0, 0, 0);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="20px" width="20px" role="img" aria-hidden="true"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"></path></svg></span></button><div data-v-1eabfd10="" class="v-menu"><!----></div><button data-v-1eabfd10="" role="button" aria-haspopup="true" aria-expanded="false" class="color-picker__main-btn"><!----></button></div></div><div class="editor__content-wrapper"><div class="editor__content tiptap-content"><div class="ProseMirror" contenteditable="false" tabindex="0"><p>History: <strong>Although named after Bloom, the publication of Taxonomy of Educational Objectives followed a series of conferences from 1949 to 1953, which were designed to improve communication between educators on the design of curricula and examinations.</strong> The first volume of the taxonomy, Handbook I: Cognitive was published in 1956, and in 1964 the second volume Handbook II: Affective was published. <strong>A revised version of the taxonomy for the cognitive domain was created in 2001.</strong></p></div></div></div></div></div><!----></div><br></div></div></div>

yury-s commented 3 years ago

Can you also show your code snippet and explain what's not working?

amit-parizian-erudite commented 3 years ago

async tryThis() { await this.page.$eval( "text=Bloom's taxonomy is a set of three", (element) => { const range1 = element.ownerDocument.createRange(); const text1 = element.firstChild; const text2 = element.firstElementChild.firstChild; range1.setStart(text1, 10); range1.setEnd(text2, 20); const selection = element.ownerDocument.defaultView.getSelection(); selection.removeAllRanges(); selection.addRange(range1); } ); }


image

that's the error I get. please note that the selector is fine. I am using it in the method I tried with the mouse. also, note what I said about the earlier solution (can't select some words, only complete text of element).

yury-s commented 3 years ago

that's the error I get. please note that the selector is fine. I am using it in the method I tried with the mouse. also, note what I said about the earlier solution (can't select some words, only complete text of element).

You need to adjust the code from my example above to fit this particular DOM tree. The reason it fails is that the selector matches <strong></element> rather than its parent. You can change the selector to match different element and/or the way you get from the selected element to its sibling. E .g. if you change your snippet to this:

await page.setContent(`<div class="v-expansion-panel-content article-content" fragment="39e34d5db0"><div class="v-expansion-panel-content__wrap"><div id="b7b6f412-d60d-31b0-c257-b306b7f00041" class="dite_container"><!----><div data-test-id="article-content" class="article-content"><div class="secondary--text content"><div class="editor"><div class="menububble" style="left: 128px; bottom: 578px;"><!----><div class="v-menu"><!----></div><button dark="" role="button" aria-haspopup="true" aria-expanded="false" class="menu__item"><img src="/img/dictionary.5482c896.svg"></button><!----><div class="d-flex justify-content-center"><!----><!----><!----></div><!----><!----><hr role="separator" aria-orientation="vertical" class="menububble__divider v-divider v-divider--inset v-divider--vertical theme--light"><div data-v-1eabfd10="" class="menu__item color-picker menu__item"><button data-v-1eabfd10="" dark="" data-test-id="highlight-color-picker-button" role="button" aria-haspopup="true" aria-expanded="false" class="menu__content__color-picker-btn color-picker__main-btn d-flex align-center"><div data-v-1eabfd10="" class="menu__content__color-picker" style="background-color: inherit;"><img src="/img/highlight.60e1b5b3.svg" width="24" height="24"></div><span data-v-1eabfd10="" aria-hidden="true" class="v-icon notranslate v-icon--svg theme--light" style="font-size: 20px; height: 20px; width: 20px; color: rgb(0, 0, 0); caret-color: rgb(0, 0, 0);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="20px" width="20px" role="img" aria-hidden="true"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"></path></svg></span></button><div data-v-1eabfd10="" class="v-menu"><!----></div><button data-v-1eabfd10="" role="button" aria-haspopup="true" aria-expanded="false" class="color-picker__main-btn"><!----></button></div></div><div class="editor__content-wrapper"><div class="editor__content tiptap-content"><div class="ProseMirror" contenteditable="false" tabindex="0"><p class=""><strong>Bloom's taxonomy is a set of three hierarchical models used to classify educational learning objectives into levels of complexity and specificity.</strong> <strong>The three lists cover the learning objectives in cognitive, affective and sensory domains.</strong> The cognitive domain list has been the primary focus of most traditional education and is frequently used to structure curriculum learning objectives, assessments and activities. The models were named after Benjamin Bloom, who chaired the committee of educators that devised the taxonomy. He also edited the first volume of the standard text, Taxonomy of Educational Objectives: The Classification of Educational Goals.</p></div></div></div></div></div><!----></div><br></div><div id="574e3162-1a5a-3031-e6af-108b123553d0" class="dite_container"><!----><div data-test-id="article-content" class="article-content"><div class="secondary--text content"><div class="editor"><div class="menububble" style="left: 0px; bottom: 0px;"><!----><div class="v-menu"><!----></div><button dark="" role="button" aria-haspopup="true" aria-expanded="false" class="menu__item"><img src="/img/dictionary.5482c896.svg"></button><!----><div class="d-flex justify-content-center"><!----><!----><!----></div><!----><!----><hr role="separator" aria-orientation="vertical" class="menububble__divider v-divider v-divider--inset v-divider--vertical theme--light"><div data-v-1eabfd10="" class="menu__item color-picker menu__item"><button data-v-1eabfd10="" dark="" data-test-id="highlight-color-picker-button" role="button" aria-haspopup="true" aria-expanded="false" class="menu__content__color-picker-btn color-picker__main-btn d-flex align-center"><div data-v-1eabfd10="" class="menu__content__color-picker" style="background-color: inherit;"><img src="/img/highlight.60e1b5b3.svg" width="24" height="24"></div><span data-v-1eabfd10="" aria-hidden="true" class="v-icon notranslate v-icon--svg theme--light" style="font-size: 20px; height: 20px; width: 20px; color: rgb(0, 0, 0); caret-color: rgb(0, 0, 0);"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="20px" width="20px" role="img" aria-hidden="true"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"></path></svg></span></button><div data-v-1eabfd10="" class="v-menu"><!----></div><button data-v-1eabfd10="" role="button" aria-haspopup="true" aria-expanded="false" class="color-picker__main-btn"><!----></button></div></div><div class="editor__content-wrapper"><div class="editor__content tiptap-content"><div class="ProseMirror" contenteditable="false" tabindex="0"><p>History: <strong>Although named after Bloom, the publication of Taxonomy of Educational Objectives followed a series of conferences from 1949 to 1953, which were designed to improve communication between educators on the design of curricula and examinations.</strong> The first volume of the taxonomy, Handbook I: Cognitive was published in 1956, and in 1964 the second volume Handbook II: Affective was published. <strong>A revised version of the taxonomy for the cognitive domain was created in 2001.</strong></p></div></div></div></div></div><!----></div><br></div></div></div>`);
await page.$eval("text=Bloom's taxonomy is a set of three", element => {
  const range1 = element.ownerDocument.createRange();
  const text1 = element.firstChild;
  const text2 = element.parentElement.lastChild;
  range1.setStart(text1, 10);
  range1.setEnd(text2, 20);
  const selection = element.ownerDocument.defaultView.getSelection();
  selection.removeAllRanges();
  selection.addRange(range1);
});

It will select the following range: image

amit-parizian-erudite commented 3 years ago

your'e right, now it works, thank you so much! you have been a great help! just a little last question, sometimes my <p> element has several children. can I reach all of them by something like firstChild or lastChild or it's only those two?

yury-s commented 3 years ago

You can use nextSibling or even chilsNodes to traverse other children. Look at DOM documentation there is a plenty of methods to get from one node to another.

amit-parizian-erudite commented 3 years ago

Thank you so much!