domcrawler loop and if statement to check if class exists - api

Hi I'm running into a little problem with DomCrawler. I'm scraping a page and it has a div with a class of .icon3d. I want to go through the page and for every div with that class I will add an "3D" item to an array, and every div without it I will add a "2D" item. Here is the code I have so far.
for ($i=0; $i < 10; $i++) {
$divs = $crawler->filter('div.icon3d');
if(count($divs)){
$type[] = '3D';
}else{
$type[] = '2D';
}
}

Check The DomCrawler Component documentation first. filter method returns filtered list of nodes, so by calling ->filter('div.icon3d') returned value will be list of all div elements which have icon3d class.
First you need to find all div elements, loop through them and add either 3D or 2D the to array depending on icon3d css class existance.
$divs = $crawler->filter('div');
foreach ($divs as $node) {
$type[] = (false !== strpos($node->getAttribute('class'), 'icon3d')) ? '3D' : '2D';
}
UPDATE
$crawler->filter('a')->each(function(Crawler $a) {
$div = $a->filter('div');
// Div exists
if ($div->count()) {
}
});
To get crawler node class use
$div->getNode(0)->getAttribute('class')

I figured it out. Here's the code I used. I'm sure there is a cleaner way.
$divs = $crawler->filter('#schedule li a')->each(function($node){
if ($node->children()->last()->attr('class') == 'icon3d') {
return '3D';
}else{
return '2D';
}
});

Related

Polymer2 Shadow dom select child element

I am working on a polymer2 shadow dom template project need to select children elements from parent elements. I found this article introduces a way to select child shadow dom elements that like this:
// No fun.
document.querySelector('x-tabs').shadowRoot
.querySelector('x-panel').shadowRoot
.querySelector('#foo');
// Fun.
document.querySelector('x-tabs::shadow x-panel::shadow #foo');
However, when I tried in my polymer2 project, like this:
//First: works!!
document.querySelector('container')
.shadowRoot.querySelector('app-grid')
.shadowRoot.querySelector('#apps');
//Second: Doesn't work!// got null
document.querySelector('container::shadow app-grid::shadow #apps')
// Thrird: document.querySelector('* /deep/ #apps') // Doesn't work, got null
I really need the second way or the third, which to put selectors in (), but both couldn't work. Does anyone know why the second one doesn't work? Thank you so much!
::shadow and /deep/ has never(?) worked in Firefox, and is depraved in Chrome 63 and later.
Source
Eric Biedelman has written a nice querySelector method for finding all custom elements on a page using shadow DOM. I wouldn't use it myself, but I have implemented it so I can "querySelect" custom elements in the console. Here is his modified code:
// EXAMPLES
// findCustomElement('app-grid') // Returns app-grid element
// findCustomElements('dom-if') // Returns an array of dom-if elements (if there are several ones)
// findCustomElement('app-grid').props // Returns properties of the app-grid element
function findCustomElement(customElementName) {
const allCustomElements = [];
customElementName = (customElementName) ? customElementName.toLowerCase() : customElementName;
function isCustomElement(el) {
const isAttr = el.getAttribute('is');
// Check for <super-button> and <button is="super-button">.
return el.localName.includes('-') || isAttr && isAttr.includes('-');
}
function findAllCustomElements(nodes) {
for (let i = 0, el; el = nodes[i]; ++i) {
if (isCustomElement(el)) {
el.props = el.__data__ || el.__data || "Doesn't have any properties";
if (customElementName && customElementName === el.tagName.toLowerCase()) {
allCustomElements.push(el);
} else if (!customElementName) {
allCustomElements.push(el);
}
}
// If the element has shadow DOM, dig deeper.
if (el.shadowRoot) {
findAllCustomElements(el.shadowRoot.querySelectorAll('*'));
}
}
}
findAllCustomElements(document.querySelectorAll('*'));
if (allCustomElements.length < 2) {
return allCustomElements[0] || customElementName + " not found";
} else if (customElementName) {
allCustomElements.props = "Several elements found of type " + customElementName;
}
return allCustomElements;
}
Remove the if (isCustomElement(el)) { statement, and you can querySelect whatever element and get an array of it if several of them exists. You can change findAllCustomElements to implement a smarter querySelect using the recursive loop on shadowDoom as base. Again, I wouldn't use this myself – and instead pass on variables from parent element(s) to children where the children have observers that activates specific behaviors – but I wanted to give you a general implementation of a fallback if nothing else works.
The problem with your question is that you don't give any specifics about WHY you want to select the children in the first place.

Radio button in polymer already selected

I'm loading a radio-button-group in polymer with a set of 4 radio buttons as answer options to a question. I have 4 questions. Now if I select an option in the first question and select next(go to the next question), the second question has its option already selected(same as the first question). Why is this happening?
<paper-radio-group>
<template is="dom-repeat" items=[[_calculateQuestionOptions(index)]]>
<paper-radio-button name="{{_calculateId(index)}}">
[[item.questionOption]]
</paper-radio-button><br>
</template>
</paper-radio-group>
CODE for next and prev operations
next() {
if (this.index < this.repos.length-1) {
this.index +=1;
}
}
prev() {
if (this.index>0) {
this.index -= 1;
}
}
To solve this issue. I wanted to add all the answers to an array and clear the selection as soon as the next button is click. Somthing like this.
next() {
var questionId = this.repos[this.index].questionId;
if (Polymer.dom(this.root).querySelector("paper-radio-group")) {
this.answers[questionId] = Polymer.dom(this.root).querySelector("paper-radio-group").selected;
Polymer.dom(this.root).querySelector("paper-radio-group").selected = null;
}
if (this.index < this.repos.length-1) {
this.index +=1;
}
questionId = this.repos[this.index].questionId;
if (this.answers[questionId] !== null) {
Polymer.dom(this.root).querySelector("paper-radio-group").selected = this.answers[questionId];
}
}
Is this the only way or is there a better way to do this.
Why is this happening?
It happens because dom-repeat reuse old elements when it update (for better performance, re-create DOM is slow). Its means only bound data in that scope will be updated.
Is this the only way or is there a better way to do this.
I think your way is already fine but you still can reduce some code by using advantage of Polymer like:
Static node map here
Complex observers here
My example see here
Thanks.

recursive repeat tree structure in Aurelia should work like accordion [Aurelia]

I need some tree structure in Aurelia. I got some link for that. It's working fine. But My requirement is like accordion with tree view. Means when I clicked on closed parent all opened parents should close and clicked one should open same as bootstrap accordion. same thing should happen When ever I clicked on child parent element with in parent repeat.
Below is my image for tree structure.
Gist run Link: Gist
The above gist is just tree structure with open and collapse. From that when I clicked on closed tree node , that should open and remaining tree nodes should be closed.
In the above gist "node-model.js" is having events for open and close. So when ever I clicked on icon the clicked event inside this variable will get only clicked node. How can I get other node in that method to hide.
Answer:
Inside your tree-view.js, add the following code (3 methods):
attached() {
window.addEventListener('goCollapseAll', (e) => {
this.closeOtherBranches(e.detail);
}, false);
}
closeOtherBranches(exceptNode) {
// traverse node tree to find current one
var found = null;
for(var i = 0; i < this.nodes.length; i++){
if (this.subSearch(this.nodes[i], exceptNode)) {
found = i;
}
}
if (found !== null) {
for(var i = 0; i < this.nodes.length; i++){
if ((i != found) && (this.nodes[i].expanded)) {
this.nodes[i].toggleNode();
}
}
}
}
subSearch(node, findNode) {
// recursive search of tree for findNode
var match = null;
if (node === findNode) {
match = node;
} else {
for(var i = 0; i < node.children.length; i++){
if (node.children[i] === findNode) {
match = node;
} else {
match = this.subSearch(node.children[i], findNode);
}
}
}
return match;
}
Then, inside your node-model.js, add the following lines at the beginning of toggleNode():
// close other node branches
if (!this.expanded) {
var event = new CustomEvent('goCollapseAll', { 'detail': this });
window.dispatchEvent(event);
}
Explanation:
When a node is expanded, it publishes a custom event to trigger the recursive search to close all nodes that are part of a different branch. It's not the prettiest solution and I think there might be a cleaner way if you adopt a different structure for the tree, but this solution definitely works well and accomplishes your purpose.
GistRun:
I've updated your GistRun to demonstrate the functionality. You can see it working here:
https://gist.run/?id=828c3c79bff0dfbaffec3252ed376c8c

How do I display invalid form Dijits inside closed TitlePanes?

I have a large Dijit-based form with many Dijits in collapsible TitlePanes.
When the form validates, any invalid items hidden inside closed TitlePanes (obviously) cannot be seen. So it appears as though the form is just dead and won't submit, though, unbeknownst to the user, there's actually an error hidden in a closed TitlePane which is preventing the form processing.
What's the solution here? Is there an easy way to simply open all TitlePanes containing Dijits that are in an error state?
If validation is done by following, it will work:-
function validateForm() {
var myform = dijit.byId("myform");
myform.connectChildren();
var isValid = myform.validate();
var errorFields = dojo.query(".dijitError");
errorFields.forEach(fieldnode){
var titlePane = getParentTitlePane(fieldnode);
//write a method getParentTitlePane to find the pane to which this field belongs
if(titlePane) {
titlePane.set('open',true);
}
}
return isValid;
}
function getParentTitlePane(fieldnode) {
var titlePane;
//dijitTitlePane is the class of TitlePane widget
while(fieldnode && fieldnode.className!="dijitTitlePane") {
fieldnode= fieldnode.parentNode;
}
if(fieldnode) {
mynode = dijit.getEnclosingWidget(fieldnode);
}
return titlePane;
}
Lets say if the following is the HTML and we call the above validateForm on submit of form.
<form id="myform" data-dojo-type="dijit/form/Form" onSubmit="validateForm();">
......
</form>
Here's what I ended up doing (I'm not great with Javascript, so this might sucked, but it works -- suggestions for improvement are appreciated):
function openTitlePanes(form) {
// Iterate through the child widgets of the form
dijit.registry.findWidgets(document.getElementById(form.id)).forEach(function(item) {
// Is this a title pane?
if (item.baseClass == 'dijitTitlePane') {
// Iterate the children of this title pane
dijit.registry.findWidgets(document.getElementById(item.id)).forEach(function(child) {
// Does this child have a validator, and -- if so -- is it valid?
if (!(typeof child.isValid === 'undefined') && !child.isValid()) {
// It's not valid, make sure the title pane is open
item.set('open', true);
}
});
}
});
}

How to exclude a field from spellcheck in sharepoint 2010?

I have a SharePointWebControls:UserField in a page layout that needs to be excluded from spell checking, as otherwise whenever a user is selected there are a large number of spelling errors are detected in the code-behind for the control.
It seems that in Sharepoint 2007 this behaviour could be implemented by using excludefromspellcheck = "true" but this doesn't seem to work for Sharepoint 2010. Has anyone come across the same problem and found a way around it?
Based on SpellCheckEntirePage.js, that appears to still be the way:
var elements=document.body.getElementsByTagName("*");
for (index=0; index < elements.length;++index)
{
if (null !=elements[index].getAttribute("excludeFromSpellCheck"))
{
continue;
}
// snipped - if (elements[index].tagName=="INPUT")
// snipped - else if (elements[index].tagName=="TEXTAREA")
}
But excludeFromSpellCheck is not a property of UserField, so it probably won't automatically copy down to the rendered HTML. When rendered, the UserField control is made up of several elements. I would try looking at the View Source to see if excludeFromSpellCheck is making it into the final HTML. But to set the attribute on the appropriate elements, you might need to use some jQuery like this:
$("(input|textarea)[id*='UserField']").attr("excludeFromSpellCheck", "true");
You can disable the spell check for certain fields by setting the "excludeContentFromSpellCheck" attribute to "true" on text area and input controls that you dont want to be spell checked.
I did this on all my page layouts. Now i dont get false positives anymore.
The solution is to add a div tag around the fields you don't want spell checked and adding a javascript that sets "excludeFromSpellCheck" to "true" for the elements within the div tag.
The solution i found is described here: Inaccurate Spell Check on SharePoint Publishing Pages
Joe Furner posted this solution, which has worked for me.
https://www.altamiracorp.com/blog/employee-posts/spell-checking-your-custom-lay
It excludes all PeoplePickers on the page:
function disableSpellCheckOnPeoplePickers() {
var elements = document.body.getElementsByTagName("*");
for (index = 0; index < elements.length; index++) {
if (elements[index].tagName == "INPUT" && elements[index].parentNode && elements[index].parentNode.tagName == "SPAN") {
var elem = elements[index];
if (elem.parentNode.getAttribute("NoMatchesText") != "") {
disableSpellCheckOnPeoplePickersAllChildren(elem.parentNode);
}
}
}
}
function disableSpellCheckOnPeoplePickersAllChildren(elem) {
try {
elem.setAttribute("excludeFromSpellCheck", "true");
for (var i = 0; i < elem.childNodes.length; i++) {
disableSpellCheckOnPeoplePickersAllChildren(elem.childNodes[i]);
}
}
catch(e) {
}
}
This code is working partially only,because if you put the people picker value again checking the people picker garbage value for one time.