I need to find out in which order two arbitrary DOM nodes appear.
Now before I start to write custom traversal code, I was wondering if there is any simple and cross browser way to find out a (comparable) location of a DOM node.
E.g.
<div> <!-- pos. 1 -->
<p> <!-- pos. 2 -->
<span>foo</span> <!-- pos. 3 -->
<span>bar</span> <!-- pos. 4 -->
</p>
<p> <!-- pos. 5 -->
<span>foo</span> <!-- pos. 6 -->
<span>bar</span> <!-- pos. 7 -->
</p>
</div>
Have you tried Node.compareDocumentPosition()? It returns a bitmask representing the calling node's relationship to the node passed as an argument. The reason it's a bitmask is because it may represent more than one of the possible relationships.
In this case, you can utilize Node.DOCUMENT_POSITION_FOLLOWING to determine the order in which two nodes appear (the result of this snippet will be in the console below the rendered page output):
const foo1 = document.getElementById("foo1");
const foo2 = document.getElementById("foo2");
if (foo1.compareDocumentPosition(foo2) & Node.DOCUMENT_POSITION_FOLLOWING) {
console.log("foo2 follows foo1");
} else {
console.log("foo2 precedes foo1");
}
<div>
<p>
<span id="foo1">foo</span>
<span>bar</span>
</p>
<p>
<span id="foo2">foo</span>
<span>bar</span>
</p>
</div>
It's widely supported, as reported by caniuse:
Related
I am trying to loop through an div element containing "data-sly-resource" to be able to include that component as a child.
I have a table element where each cell has an individual authoring interface using a child component. I get two counts that is rowCount as array of "x" and columnCount as array of "y". I'm able to iterate through the class="table-cell" and am able to add "y" number of columns with their respective child components using data-sly-resource . But when I'm trying to iterate through class="table-row" using rowcount it only iterates the content and adds "y" number of rows but doesn't add child components in the rows
thank you
<div class="table">
<sly data-sly-list.card="${rowCount.array}">
<div class="table-row">
<sly data-sly-list.card="${columnCount.array}">
<div class="table-cell" data-sly-test.resourceNode="${['cell', card.intValue] #join='-'}">
<div data-sly-resource="${ #path=resourceNode,
resourceType='example/components/content/tableLongForm/rte'}"
data-sly-unwrap="${!wcmmode.edit && !wcmmode.preview}" style="width:60px;"></div>
</div>
</sly>
</div>
</sly>
</div>
Authoring Interface Image
You are using the same identifier card for the list items in the nested loops, perhaps you meant it as:
<div class="table">
<sly data-sly-list.row="${rowCount.array}">
<div class="table-row">
<sly data-sly-list.card="${columnCount.array}">
<div class="table-cell" data-sly-test.resourceNode="${['cell', row.intValue, card.intValue] # join='-'}">
<div data-sly-resource="${ #path=resourceNode,
resourceType='example/components/content/tableLongForm/rte'}"
data-sly-unwrap="${!wcmmode.edit && !wcmmode.preview}" style="width:60px;"></div>
</div>
</sly>
</div>
</sly>
</div>
How to check the empty list on Sightly?
I wanted to prevent render the item-list DIV if there was no item on itemImgaeList. But it returns me one (1) always if there were no items while trying with -
LIST_SIZE_PRINT = "${container.itemImgaeList.size}"; // retrun 1
HTL:
<div data-sly-test="${container.itemImgaeList.size > 1}">
<sly data-sly-list.imageList="${container.itemImgaeList}">
<div class="item-list">
<picture>
<img alt="${imageList.qlImageText}" src="${imageList.qlImagePath}" />
</picture>
</div>
</sly>
</div>
Any help?
data-sly-list can be used for implementing the above requirement of rendering the list elements only when the list is not empty.
The use of 'data-sly-test' is not required for checking a list, as the check for emptiness is done inherently by data-sly-list.
Here is a working example using data-sly-list:
<div class="item-list" data-sly-list.item="${container.itemImgaeList}">
<picture>
<img alt="${item.qlImageText}" src="${item.qlImagePath}" />
</picture>
</div>
More information:
https://www.aemquickstart.in/2016/08/htl-sightly-notes.html
Using the following Protractor element and by.repeater() API methods below:
var targetRowText = 'Sales';
var targetGridName = 'myGrid';
var sel = 'grid-directive[grid-name="' + targetGridName + '"] .col-freeze .grid-wrapper';
var gridRows = element(by.css(sel).all(by.repeater('row in vm.sourceData.data'));
var result = gridRows.all(by.cssContainingText('span', targetRowText)).first();
I am able to select the following row element from a grid which I have labeled, myGrid:
<div id="rowId_21" ng-class-odd="'row-2'" ng-class-even="'row-3'" ng-class="vm.hideRow(row)" class="row-3 height-auto">
<div ng-repeat="column in vm.sourceData.columns" >
<div ng-if="!column.subCols" class="ng-scope">
<div ng-if="row[column.field].length !== 0" class="ng-scope highlight21">
<span ng-bind-html="row[column.field] | changeNegToPrenFormat" vm.highlightedrow="" class="ng-binding">
Sales
</span>
</div>
</div>
</div>
</div>
Please note that I have used by.cssContainingText() to look up the "Sales" span element.
MY PROBLEM:
That that I have located this row in var result, how can I retrieve the id attribute of that outer-most div ?
In other words, I need to selected <div id="rowId_21" so that I can reuse id="rowId_21" in a subsequent Protractor selector.
In jQuery, for example, I could use brute force to get that outer div id as follows :
var el = $('grid-directive[grid-name="Sales"] .col-freeze .grid-wrapper #rowId_21 span')
el.parentElement.parentElement.parentElement.parentElement;
Here's a high-level outlines of what I mean. The grid actually separates the left-most column from the actual data rows, so there are two distinct divs that accomplish this:
<div grid-directive grid-name="myGrid">
<div class="col-freeze" >
<!-- CONTAINS LEFT-MOST "CATEGORIES" COLUMN -->
</div>
<div class="min-width-grid-wrapper">
<!-- CONTAINS THE DATA ROWS-->
</div>
However, I'm struggling to do this in Protractor.
Advice is appreciated...
Bob
A straight-forward option would be to get to the desired parent element using the ancestor axis:
element(by.xpath("./ancestor::div[starts-with(#id, 'rowId')]")).getAttribute("id").then(function (parentId) {
// use parentId here
});
Though, I think that this going down and then up the tree should be considered as a sign that you are not approaching the problem in an easy and correct way.
I have a fluid typo3 site with single page layout.One menu have different layout.How to get the selected layout name in my main template?
main.html
<v:page.menu levels="1" as="sections">
<f:for each="{sections}" as="section" iteration="itemIteration">
<f:debug>{sections}</f:debug>
</f:for>
</v:page.menu>
sub.html
<f:layout name="Pagewithnav" />
<f:section name="Configuration">
<flux:form id="subnav" icon="{f:uri.resource(path: 'Icons/Page/logo.png')}" label="Sub Navigation">
<!-- Insert fields, sheets, grid, form section objects etc. here, in this flux:form tag -->
</flux:form>
<flux:grid>
<!-- Edit this grid to change the "backend layout" structure -->
<flux:grid.row>
<flux:grid.column colPos="0" colspan="4" name="main" label="Navigation wrapper" />
</flux:grid.row>
<flux:grid.row>
<flux:grid.column colPos="1" colspan="4" name="main" label="Main wrapper" />
</flux:grid.row>
</flux:grid>
</f:section>
<f:section name="Main">
<div class="sub_nav">
<div class="container">
<v:content.render column="0"/>
</div>
</div>
<div class="container">
<v:content.render column="1"/>
</div>
</f:section>
It depends on what exactly you mean by "Layout name":
If you mean fields of the pages table for each individual page then those will be available in each record when you iterate it in v:page.menu (for example, tx_fed_page_controller_action).
If you mean fields of pages but also when this variable is inherited (for example, backend_layout is and tx_fed_page_controller_action is as well, but each through different methods) it becomes more complex: you would need to walk through the root line and use the first non-empty value. For that, v:page.rootLine plus v:iterator.filter plus v:iterator.first can be of great help (put output from root line into filter to remove non-empty values then use the first VH to select the first value from that filtered result).
If you define this "layout" in a Flux form field on your page template it becomes a FlexForm variable which you can read with flux:form.data (with inheritance if not disabled and if templates match up through root line).
Depending on exactly which you mean there are quite a few possible solutions. If you are also looking for a recommendation about which one it makes sense to use: most likely you mean the layout you select in page properties (regardless of field) and for this, v:page.rootLine plus v:iterator.filter plus v:iterator.first is a nice and generic method to "slide select" any non-empty value from the root line of the current page.
You can get it with a variable in fluid
lib.templateName = TEXT
lib.templateName.stdWrap.cObject = CASE
lib.templateName.stdWrap {
cObject = TEXT
cObject {
data = levelfield:-2,backend_layout_next_level,slide
override.field = backend_layout
split {
token = pagets__
1.current = 1
1.wrap = |
}
}
ifEmpty = Default
}
page = PAGE
page.10 = FLUIDTEMPLATE
page.10 {
#...
variables.templateName < lib.templateName
}
I've been looking at examples of nested foreach loops in knockout all afternoon, and I haven't been able to get anything working. My current setup, at least the relevant parts, are below.
ViewModel:
var sample = {
this.pageData = ko.observable();
this.panels = ko.observableArray();
};
ko.utils.extend(sample.prototype, {
activate: {
this.pageData(sampleData);
this.panels([
{
name: 'column1',
keys: ['key1', 'key2', 'key3'],
loadedWidgets: ko.observableArray()
},
{
name: 'column2',
keys: ['key4', 'key5'],
loadedWidgets: ko.observableArray()
},
{
name: 'column3',
keys: ['key6'],
loadedWidgets: ko.observableArray()
}
]);
this.loadWidgetPanels(this.panels(), this.pageData());
},
loadWidgetPanels: function (panels, pageData) {
for (var i = 0; i < panels.length; i++) {
var screens = filterContentByKey(pageData.Content, panels[i].keys);
if (screens) {
panels[i].loadedWidgets.push(widgetFactory.getWidgets(screens));
}
}
}
}
View:
<div>
<!-- ko foreach: panels -->
<div class="3columnStyle">
<!-- ko foreach: loadedWidgets -->
<!--ko compose: $data --><!-- /ko -->
<!-- /ko -->
</div>
<!-- /ko -->
</div>
I've confirmed that I'm getting back the right data in the right format in my loadedWidgets, but they don't display in the view. I can tell that the view at least knows how much data is there, because my DOM has a ko compose element for each widget. For example, the first column has a handful of widgets, and that div gets created with a handful of widgets in it. Column 2 has 2 widgets, and it gets 2 compose elements. Column 3 has 1 widget and gets one element. It's just not displaying the widgets themselves. Do I need additional elements or additional binding somewhere?
I have a working model of this that doesn't rely on nested loops. Instead of using the array of objects, it just creates each of the observable arrays. In other words, it's not looping. Instead of one array containing three objects, each with its own array, I have three arrays:
this.column1Widgets();
this.column2Widgets();
this.column3Widgets();
They're constructed the same way, just manually instead of looping. And the View looks more like this:
<div class="3columnStyle">
<!-- ko foreach: column3Widgets -->
<!-- ko compose: $data --><!-- /ko -->
<!-- /ko -->
</div>
<div class="3columnStyle">
<!-- ko foreach: column3Widgets -->
<!-- ko compose: $data --><!-- /ko -->
<!-- /ko -->
</div>
<div class="3columnStyle">
<!-- ko foreach: column3Widgets -->
<!-- ko compose: $data --><!-- /ko -->
<!-- /ko -->
</div>
I'm not sure why it's not working with the nested loop, but since the data is identical, I'm sure there's something I'm missing in setting up the View.
Without seeing the rest of your code, it's difficult to tell for sure. I'm a little suspicious of the sample object in your viewmodel. But it seems to me that you're not actually nesting your foreach's.
In your view, replace
foreach: loadedWidgets
with
foreach: $data.loadedWidgets
You need to reference the parent foreach in some way. $data represents the current item in the parent foreach, and that item, if I understand your model correctly, contains a loadedWidgets key.
Also, there's no need for containerless binding in your case.
As Eric Taylor suggested, it must have something to do with the containerless binding. I created a jsfiddle with some oversimplified object models, but changing my DOM from the above to the following immediately fixed the issue:
<div>Test</div>
<div data-bind="foreach: panels">
<ul data-bind="foreach: loadedWidgets">
<li data-bind="text: $data"></li>
</ul>
</div>
I don't think I have a good grasp of how the containers interact with the binding yet.