Занимавам се с предна разработка достатъчно дълго, за да видя тенденция през годините: по-млади разработчици работят с нова парадигма на програмиране, без да разбират историческия й контекст. Разбира се, напълно разбираемо е да не знаете нещо. Мрежата е много голямо място с разнообразен набор от умения и специалности и ние не винаги знаем какво не знаем. Ученето в тази област е по-скоро непрекъснато пътуване, отколкото нещо, което се случва веднъж и свършва. Примерен случай: Някой от моя екип попита дали е възможно да се разбере дали потребителите се отдалечават от определен раздел в потребителския интерфейс. Посочих събитието beforeunload на JavaScript. Но онези, които са се занимавали с това преди, знаят, че това е възможно, защото са били засегнати от предупреждения за незапазени данни на други сайтове, за които beforeunload е типичен случай на употреба. Също така посочих събитията pageHide и visibilityChange на моя колега за добра мярка. Как разбрах за това? Защото се появи в друг проект, а не защото го изучавах, когато първоначално изучавах JavaScript. Факт е, че модерните предни рамки стоят на раменете на технологичните гиганти, които ги предшестваха. Те абстрахират практиките за разработка, често за по-добро изживяване на разработчиците, което намалява или дори елиминира необходимостта да се знае или да се докосне до това, което традиционно са били основни концепции за предния край, които всеки вероятно трябва да знае. Помислете за CSS обектния модел (CSSOM). Може да очаквате, че всеки, който работи в CSS и JavaScript, има богат практически опит в CSSOM, но това не винаги ще е така. Имаше проект React за сайт за електронна търговия, върху който работих, където трябваше да заредим таблица със стилове за текущо избрания доставчик на плащания. Проблемът беше, че таблицата със стилове се зареждаше на всяка страница, когато беше наистина необходима само на конкретна страница. Разработчикът, натоварен да направи това да се случи, никога не е зареждал динамично таблица със стилове. Отново, това е напълно разбираемо, когато React абстрахира традиционния подход, към който може да сте достигнали. CSSOM вероятно не е нещо, от което се нуждаете в ежедневната си работа. Но е вероятно да се наложи да взаимодействате с него в даден момент, дори и в еднократен случай. Тези преживявания ме вдъхновиха да напиша тази статия. Има много съществуващи уеб функции и технологии в дивата природа, които може никога да не докоснете директно в ежедневната си работа. Може би сте сравнително нов в уеб разработката и просто не сте наясно с тях, защото сте потънали в абстракцията на конкретна рамка, която не изисква да я познавате задълбочено или дори изобщо. Говоря специално за XML, който много от нас знаят, че е древен език, който не е напълно различен от HTML. Повдигам това поради скорошни дискусии на WHATWG, които предполагат, че значителна част от XML стека, известен като XSLT програмиране, трябва да бъде премахнат от браузърите. Това е точно вид по-стара, съществуваща технология, която имаме от години, която може да се използва за нещо толкова практично като ситуацията на CSSOM, в която се намираше моят екип. Работили ли сте с XSLT преди? Нека да видим дали ще се облегнем много на тази по-стара технология и ще използваме нейните функции извън контекста на XML, за да се справим с проблеми от реалния свят днес. XPath: Централният API Най-важната XML технология, която е може би най-полезна извън директната XML перспектива, е XPath, език за заявки, който ви позволява да намерите всеки възел или атрибут в дърво за маркиране с един коренов елемент. Имам лична привързаност към XSLT, но това също разчита на XPath и личната привързаност трябва да се остави настрана при класирането по важност. Аргументът за премахване на XSLT не споменава XPath, така че предполагам, че все още е разрешен. Това е добре, защото XPath е централният и най-важен API в този набор от технологии, особено когато се опитвате да намерите нещо, което да използвате извън нормалното използване на XML. Важно е, защото докато CSS селекторите могат да се използват за намиране на повечето елементи във вашата страница, те не могат да ги намерят всички. Освен това CSS селекторите не могат да се използват за намиране на елемент въз основа на текущата му позиция в DOM. XPath може. Сега, някои от вас, които четете това, може би знаят XPath, а други може би не. XPath е доста голяма област на технологията и не мога наистина да науча всички основи и също така да ви покажа страхотни неща, които можете да правите с него в една статия като тази. Всъщност се опитах да напиша тази статия, но средната публикация в Smashing Magazine не надхвърля 5000 думи. Вече бях на повече от2000 думи, но само на половината от основните неща. И така, ще започна да правя страхотни неща с XPath и ще ви дам някои връзки, които можете да използвате за основите, ако намирате тези неща за интересни. Комбиниране на XPath и CSS XPath може да прави много неща, които CSS селекторите не могат, когато заявяват елементи. Но CSS селекторите също могат да правят няколко неща, които XPath не може, а именно да правят заявки за елементи по име на клас.
CSS XPath .myClass /*[съдържа(@class, "myClass")]
В този пример CSS прави заявки за елементи, които съдържат име на клас .myClass. Междувременно примерът на XPath прави заявки за елементи, които съдържат клас атрибут с низа „myClass“. С други думи, той избира елементи с myClass във всеки атрибут, включително елементи с .myClass име на клас - както и елементи с "myClass" в низа, като .myClass2. XPath е по-широк в този смисъл. Така че не. Не предполагам, че трябва да изхвърлим CSS и да започнем да избираме всички елементи чрез XPath. Не е това въпросът. Въпросът е, че XPath може да прави неща, които CSS не може и все още може да бъде много полезен, въпреки че е по-стара технология в стека на браузъра и може да не изглежда очевидно на пръв поглед. Нека използваме двете технологии заедно не само защото можем, но и защото ще научим нещо за XPath в процеса, превръщайки го в друг инструмент във вашия стек – такъв, който може би не сте знаели, че е бил там през цялото време! Проблемът е, че методът document.evaluate на JavaScript и различните методи за избор на заявки, които използваме с CSS API за JavaScript, са несъвместими. Направих съвместим API за заявки, за да започнем, въпреки че трябва да призная, че не съм мислил много върху него, тъй като това е отклонение от това, което правим тук. Ето един доста прост работещ пример за многократно използваем конструктор на заявки: Вижте Pen queryXPath [forked] от Bryan Rasmussen. Добавих два метода към обекта на документа: queryCSSSelectors (който по същество е querySelectorAll) и queryXPaths. И двете връщат обект queryResults:
{ тип заявка: възли | низ | номер | булево, резултати: всякакви[] // html елементи, xml елементи, низове, числа, булеви стойности, queryCSSSelectors: (заявка: низ, изменение: булево) => queryResults, queryXpaths: (заявка: низ, изменение: булево) => queryResults }
Функциите queryCSSSelectors и queryXpaths изпълняват заявката, която им давате върху елементите в масива с резултати, стига масивът с резултати да е от тип възли, разбира се. В противен случай ще върне queryResult с празен масив и тип възли. Ако свойството за изменение е зададено на true, функциите ще променят собствените си queryResults. При никакви обстоятелства това не трябва да се използва в производствена среда. Правя го по този начин само за да демонстрирам различните ефекти от използването на двата API за заявки заедно. Примерни заявки Искам да покажа няколко примера за различни XPath заявки, които демонстрират някои от мощните неща, които могат да правят и как могат да се използват вместо други подходи. Първият пример е //li/text(). Това прави заявки за всички li елементи и връща техните текстови възли. И така, ако поискаме следния HTML:
- един
- два
- три
...ето какво се връща:
{"queryType":"xpathEvaluate","results":["one","two","three"],"resultType":"string"}
С други думи, получаваме следния масив: ["one","two","three"]. Обикновено бихте направили заявка за елементите li, за да получите това, превърнали резултата от тази заявка в масив, картографирали масива и върнали текстовия възел на всеки елемент. Но можем да направим това по-сбито с XPath: document.queryXPaths("//li/text()").резултати.
Забележете, че начинът да получите текстов възел е да използвате text(), който изглежда като сигнатура на функция - и това е така. Връща текстовия възел на елемент. В нашия пример има три елемента li в маркирането, всеки от които съдържа текст ("one", "two" и "three").
Нека да разгледаме още един пример за заявка text(). Да приемем, че това е нашата маркировка:
Нека напишем заявка, която връща стойността на атрибута href: document.queryXPaths("//a[text() = 'Sign In']/@href").results.
Това е XPath заявка за текущия документ, точно като последния пример, но този път връщаме атрибута href на връзка (елемент), който съдържа текста „Вход“. Действително върнаторезултатът е ["/login.html"]. Общ преглед на функциите на XPath Има редица функции на XPath и вероятно не сте запознати с тях. Мисля, че има няколко, за които си струва да знаете, включително следното:
starts-withАко даден текст започва с конкретен друг текстов пример, starts-with(@href, 'http:') връща true, ако атрибут href започва с http:. съдържа Ако даден текст съдържа конкретен друг текстов пример, contains(text(), "Smashing Magazine") връща true, ако текстов възел съдържа думите "Smashing Magazine" навсякъде. countВръща брой на съвпаденията на заявка. Например count(//*[starts-with(@href, 'http:']) връща брой на това колко връзки в контекстния възел имат елементи с href атрибут, който съдържа текста, започващ с http:. substringРаботи като JavaScript substring, с изключение на това, че предавате низа като аргумент. Например substring("my text", 2, 4) връща "y t". substring-before Връща частта от низ преди друг низ. Например substing-before("my text", " ") връща "my". По същия начин substring-before("hi","bye") връща празен низ. substring-after Връща частта от низ след друг низ. Например substing-after("моят текст", " ") връща "текст". По подобен начин substring-after("hi","bye") връща празен низ. normalize-space Връща аргументния низ с празно пространство, нормализирано чрез премахване на началния и завършващия празен интервал и замяна на поредици от празни знаци с единичен интервал. notВръща булево true, ако аргументът е false, в противен случай false. true Връща булево true. false Връща boolean false. concat Същото нещо като JavaScript concat, само че не го изпълнявате като метод на низ. Вместо това поставяте всички низове, които искате да свържете. string-lengthThis не е същото като JavaScript string-length, а по-скоро връща дължината на низа, който му е даден като аргумент. translateThis взема низ и променя втория аргумент на третия аргумент. Например translate("abcdef", "abc", "XYZ") извежда XYZdef.
Освен тези специфични функции на XPath, има редица други функции, които работят по същия начин като техните двойници на JavaScript - или двойници в почти всеки език за програмиране - които вероятно също ще намерите за полезни, като под, таван, кръг, сума и т.н. Следната демонстрация илюстрира всяка от тези функции: Вижте числените функции на Pen XPath [forked] от Bryan Rasmussen. Имайте предвид, че както повечето функции за манипулиране на низове, много от числовите приемат един вход. Това е, разбира се, защото те трябва да се използват за заявки, както в последния пример на XPath: //li[floor(text()) > 250]/@val
Ако ги използвате, както правят повечето примери, в крайна сметка ще го стартирате на първия възел, който съответства на пътя. Има и някои функции за преобразуване на типове, които вероятно трябва да избягвате, защото JavaScript вече има свои собствени проблеми с преобразуването на типове. Но може да има моменти, когато искате да конвертирате низ в число, за да го сравните с друго число. Функциите, които задават типа на нещо, са булево, число, низ и възел. Това са важните типове данни XPath. И както можете да си представите, повечето от тези функции могат да се използват върху типове данни, които не са DOM възли. Например substring-after приема низ, както вече разгледахме, но това може да е низ от атрибут href. Може да бъде и просто низ:
const testSubstringAfter = document.queryXPaths("substring-after('hello world',' ')");
Очевидно този пример ще ни върне масива с резултати като ["свят"]. За да покажа това в действие, направих демонстрационна страница, използваща функции срещу неща, които не са DOM възли: Вижте Pen queryXPath [forked] от Bryan Rasmussen. Трябва да отбележите изненадващия аспект на функцията за превод, който е, че ако имате символ във втория аргумент (т.е. списъкът със знаци, които искате да бъдат преведени) и няма съответстващ знак, към който да се преведе, този знак се премахва от изхода. По този начин това:
translate('Здравейте, казвам се Иниго Монтоя, вие убихте баща ми, пригответе се да умрете','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,','*')
...резултати в низа, включително интервали: [" * * ** "]
Това означава, че буквата „a“ се превежда в звездичка (*), но всеки друг знак, който няма превод, даден на целевия низ, се премахва напълно. Празното пространство е всичко, което ни оставамежду преведените знаци „a“. След това отново тази заявка:
translate('Здравейте, казвам се Иниго Монтоя, вие убихте баща ми, пригответе се да умрете','abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,','************************************************************')")
...няма проблем и извежда резултат, който изглежда така:
"***** ** **** ** ***** ******* *** ****** ** ****** ******* ** ***"
Може да ви направи впечатление, че няма лесен начин в JavaScript да направите точно това, което прави функцията за превод на XPath, въпреки че за много случаи на употреба replaceAll с регулярни изрази може да се справи. Можете да използвате същия подход, който демонстрирах, но това е неоптимално, ако всичко, което искате, е да преведете низовете. Следната демонстрация обгръща функцията за превод на XPath, за да предостави версия на JavaScript: Вижте функцията за превод на писалка [forked] от Bryan Rasmussen. Къде можете да използвате нещо подобно? Помислете за криптиране на Caesar Cipher с триместно отместване (напр. най-високо криптиране от 48 г. пр.н.е.):
translate("Цезар планира да премине Рубикон!", „ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz“, "XYZABCDEFGHIJKLMNOPQRSTUVWxyzabcdefghijklmnopqrstuvw")
Входящият текст „Цезар планира да премине Рубикон!“ води до „Zxbpxo fp mixkkfkd ql zolpp qeb Oryfzlk!“ За да дам още един бърз пример за различни възможности, направих метална функция, която приема входен низ и използва функция за превод, за да върне текста, включително всички знаци, които приемат умлаути. Вижте функцията Pen metal [forked] от Bryan Rasmussen.
const метал = (str) => { return translate(str, "AOUaou", "ÄÖÜäöü"); }
И ако се даде текстът „Motley Crüe rules, rock on dudes!“, връща „Mötley Crüe rüles, röck ön düdes!“ Очевидно може да има всякакви пародийни употреби на тази функция. Ако това сте вие, тогава тази статия на TVTropes трябва да ви осигури много вдъхновение. Използване на CSS с XPath Спомнете си нашата основна причина да използваме CSS селектори заедно с XPath: CSS до голяма степен разбира какво е клас, докато най-доброто, което можете да направите с XPath, е да сравнявате низове на атрибута клас. Това ще работи в повечето случаи. Но ако някога попаднете в ситуация, в която, да речем, някой е създал класове с имена .primaryLinks и .primaryLinks2 и вие използвате XPath, за да получите класа .primaryLinks, тогава най-вероятно ще срещнете проблеми. Докато няма нищо такова глупаво, вероятно ще използвате XPath. Но със съжаление трябва да съобщя, че съм работил на места, където хората правят такива глупави неща. Ето още една демонстрация, използваща заедно CSS и XPath. Показва какво се случва, когато използваме кода, за да изпълним XPath на контекстен възел, който не е възелът на документа. Вижте Pen css и xpath заедно [forked] от Bryan Rasmussen. CSS заявката е .relatedarticles a, която извлича двата a елемента в div, присвоен на .relatedarticles клас. След това са три „лоши“ заявки, тоест заявки, които не правят това, което искаме да правят, когато се изпълняват с тези елементи като контекстен възел. Мога да обясня защо се държат по-различно от очакваното. Въпросните три лоши запитвания са:
//text(): Връща целия текст в документа. //a/text(): Връща целия текст вътре във връзките в документа. ./a/text(): Не връща резултати.
Причината за тези резултати е, че докато вашият контекст е елементи, върнати от CSS заявката, // противоречи на целия документ. Това е силата на XPath; CSS не може да премине от възел до предшественик и след това до брат на този предшественик и да се спусне до потомък на този брат. Но XPath може. Междувременно ./ отправя заявки към децата на текущия възел, където точката (.) представлява текущия възел, а наклонената черта (/) представлява преминаване към някакъв дъщерен възел - дали е атрибут, елемент или текст се определя от следващата част от пътя. Но няма дъщерен елемент, избран от CSS заявката, следователно тази заявка също не връща нищо. Има три добри заявки в последната демонстрация:
.//текст(), ./текст(), normalize-space(./text()).
Заявката за normalize-space демонстрира използването на XPath функция, но също така коригира проблем, включен в другите заявки. HTML е структуриран по следния начин:
Автоматизиране на тестването на вашите функции със Selenium WebDriver
Заявката връща подаване на ред в началото и края на текстовия възел,и normalize-space премахва това. Използването на която и да е XPath функция, която връща нещо различно от булево с входен XPath, се прилага за други функции. Следната демонстрация показва няколко примера: Вижте примерите за функциите на Pen xpath [forked] от Bryan Rasmussen. Първият пример показва проблем, за който трябва да внимавате. По-конкретно, следният код:
document.queryXPaths("substring-after(//a/@href,'https://')");
... връща един низ:
"www.smashingmagazine.com/2018/04/feature-testing-selenium-webdriver/"
Има смисъл, нали? Тези функции не връщат масиви, а по-скоро единични низове или единични числа. Изпълнението на функцията навсякъде с множество резултати връща само първия резултат. Вторият резултат показва какво наистина искаме:
document.queryCSSSelectors("a").queryXPaths("substring-after(./@href,'https://')");
Което връща масив от два низа:
["www.smashingmagazine.com/2018/04/feature-testing-selenium-webdriver/","www.smashingmagazine.com/2022/11/automated-test-results-improve-accessibility/"]
XPath функциите могат да бъдат вложени точно като функциите в JavaScript. Така че, ако знаем структурата на URL адреса на списание Smashing, можем да направим следното (препоръчва се използването на шаблонни литерали): `превод( подниз( подниз-след(./@href, ‘www.smashingmagazine.com/’) ,9), '/','')`
Това става твърде сложно до степен, че се нуждае от коментари, описващи какво прави: вземете целия URL от атрибута href след www.smashingmagazine.com/, премахнете първите девет знака, след това преведете знака наклонена черта (/) в нищо, за да се отървете от крайната наклонена черта. Полученият масив:
["feature-testing-selenium-webdriver","automated-test-results-improve-accessibility"]
Още случаи на използване на XPath XPath наистина може да блесне при тестване. Причината не е трудно да се види, тъй като XPath може да се използва за получаване на всеки елемент в DOM от всяка позиция в DOM, докато CSS не може. Не можете да разчитате, че CSS класовете ще останат последователни в много съвременни системи за изграждане, но с XPath можем да направим по-стабилни съвпадения за това какво е текстовото съдържание на даден елемент, независимо от променящата се DOM структура. Има изследвания върху техники, които ви позволяват да правите устойчиви XPath тестове. Нищо не е по-лошо от това тестовете да се развалят и да се провалят само защото CSS селекторът вече не работи, защото нещо е преименувано или премахнато. XPath също е наистина страхотен при извличане на множество локатори. Има повече от един начин за използване на XPath заявки за съпоставяне на елемент. Същото важи и за CSS. Но XPath заявките могат да проникнат в нещата по по-целенасочен начин, който ограничава това, което се връща, което ви позволява да намерите конкретно съвпадение, където може да има няколко възможни съвпадения. Например, можем да използваме XPath, за да върнем конкретен h2 елемент, който се съдържа в div, който непосредствено следва братски div, който от своя страна съдържа дъщерен елемент на изображение с атрибут data-testID="leader" върху него:
не получавайте това заглавие
Не получавайте и това заглавие
Заглавката за водещото изображение
Това е запитването: document.queryXPaths(` //div[ следващ брат::div[1] /img[@data-testID='лидер'] ] /h2/ текст() `);
Нека пуснем демонстрация, за да видим как всичко това се съчетава: Вижте Pen Complex H2 Query [forked] от Bryan Rasmussen. Така че, да. Има много възможни пътища към всеки елемент в тест, използващ XPath. Отмяна на XSLT 1.0 Споменах в началото, че екипът на Chrome планира да премахне поддръжката на XSLT 1.0 от браузъра. Това е важно, защото XSLT 1.0 използва ориентирано към XML програмиране за трансформиране на документи, което от своя страна разчита на XPath 1.0, което се намира в повечето браузъри. Когато това се случи, ще загубим ключов компонент на XPath. Но предвид факта, че XPath е наистина страхотен за писане на тестове, смятам, че е малко вероятно XPath като цяло да изчезне скоро. Въпреки това забелязах, че хората се интересуват от функция, когато тя бъде отнета. И това със сигурност е вярно в случай на отхвърляне на XSLT 1.0. В Hacker News се провежда цяла дискусия, пълна с аргументи срещу оттеглянето. Самата публикация е чудесен пример за създаване на рамка за блогове с XSLT. Виеможете да прочетете дискусията за себе си, но тя навлиза в това как JavaScript може да се използва като подложка за XLST за справяне с такива случаи. Също така видях предложения, че браузърите трябва да използват SaxonJS, който е порт към Saxon XSLT, XQUERY и XPath двигателите на JavaScript. Това е интересна идея, особено като се има предвид, че Saxon-JS прилага текущата версия на тези спецификации, докато няма браузър, който да прилага версия на XPath или XSLT след 1.0, и нито един, който да прилага XQuery. Свързах се с Норм Тови-Уолш в Saxonica, компанията зад SaxonJS и други версии на двигателя Saxon. Той каза: „Ако някой доставчик на браузър се интересува от SaxonJS като отправна точка за интегриране на модерни XML технологии в браузъра, ще се радваме да го обсъдим с него.“ – Норм Тови-Уолш
Но също така добави: "Ще бъда много изненадан, ако някой си помисли, че вземането на SaxonJS в сегашната му форма и пускането му в компилацията на браузъра непроменен би бил идеалният подход. Доставчик на браузър, поради естеството на факта, че изгражда браузъра, може да подходи към интеграцията на много по-дълбоко ниво, отколкото ние можем "отвън"."- Норм Тови-Уолш
Заслужава да се отбележи, че коментарите на Tovey-Walsh дойдоха около седмица преди съобщението за оттегляне на XSLT. Заключение Мога да продължа още. Но се надявам, че това демонстрира силата на XPath и ви даде много примери, демонстриращи как да го използвате за постигане на страхотни неща. Това е перфектен пример за по-стара технология в стека на браузъра, която все още има много полезност днес, дори ако никога не сте знаели, че съществува или никога не сте обмисляли да посегнете към нея. Допълнително четене
„Подобряване на устойчивостта на автоматизирани уеб тестове с естествен език“ (ACM Digital Library) от Maroun Ayli, Youssef Bakouny, Nader Jalloul и Rima Kilany. Тази статия предоставя много XPath примери за писане на устойчиви тестове. XPath (MDN) Това е отлично място за начало, ако искате техническо обяснение, описващо как работи XPath. Урок за XPath (ZVON) Открих, че този урок е най-полезен в собственото ми обучение, благодарение на изобилието от примери и ясни обяснения. XPather Този интерактивен инструмент ви позволява да работите директно с кода.