Click on second element with CasperJS - xpath

I want to use CasperJS to click over the second element in a document.
My Document:
<div class="sample secondClass">1</div>
<div class="sample">2
<ul class="ul">
<li class="li">
<a class="link">
<div class="div_msg">
<span>AbCde</span>
</div>
</a>
</li>
</ul>
<div class="sample secondClass">3</div>
This is what I have tried:
casper.thenClick(x('//*[#class="sample"][2]/ul[#class="ul"]/li[#class="li"]/a[#class="link"]/div[#class="div_msg"]/span[string-length(text())="ABCDE"]'), function() {
...
//do anythings
...
}

x(...) is a convenience function to use XPath expressions with almost all CasperJS functions instead of CSS selectors. :nth-child(1) is a CSS selector, that doesn't work in XPath expressions.
You can select the position of an element among its matched siblings with [x]. If you want the second element, then use x('//*[#class="sample"][2]') (XPaths and CSS selectors are 1-indexed and not 0-index like basically all other programming languages).
colorMatchingRed('//*[#class="sample"][2]')
function colorMatchingRed(xpath) {
var spans = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0 ; i < spans.snapshotLength; i++ ) spans.snapshotItem(i).style.color = "red";
}
<div>
<div class="sample">No</div>
<div class="sample">Yes</div>
<div class="sample">No</div>
</div>
<hr/>
<div>
<div>No</div>
<div class="sample">No</div>
<div class="sample">Yes</div>
<div class="sample">No</div>
</div>
You can also use x('//*[#class="sample" and position()=2]') which has a slightly different meaning, because it matches the second element among its siblings that also has a specific #class attribute.
colorMatchingRed('//*/*[#class="sample" and position()=2]')
function colorMatchingRed(xpath) {
var spans = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0 ; i < spans.snapshotLength; i++ ) spans.snapshotItem(i).style.color = "red";
}
<div>
<div class="sample">No</div>
<div class="sample">Yes</div>
<div class="sample">No</div>
</div>
<hr/>
<div>
<div>No</div>
<div class="sample">Yes</div>
<div class="sample">No</div>
<div class="sample">No</div>
</div>
If the elements are not siblings of one another, then you can also evaluate the node set and select a specific element from that set: x('(//*[#class="sample"])[2]'). It creates a set of all elements with that specific #class and chooses the second one.
colorMatchingRed('(//*[#class="sample"])[2]')
function colorMatchingRed(xpath) {
var spans = document.evaluate(xpath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0 ; i < spans.snapshotLength; i++ ) spans.snapshotItem(i).style.color = "red";
}
<div>
<div class="sample">No</div>
<div class="sample">Yes</div>
<div class="sample">No</div>
</div>
<hr/>
<div>
<div>No</div>
<div class="sample">No</div>
<div class="sample">No</div>
<div class="sample">No</div>
</div>
Here's the correct click call for the updated question:
casper.thenClick(x('//*[#class="sample"][2]/ul[#class="ul"]/li[#class="li"]/a[#class="link"]/div[#class="div_msg"]/span[string-length(text())=string-length("ABCDE")]'), ...)
or shorter:
casper.thenClick(x('//*[#class="sample"][2]//div[#class="div_msg"]/span[string-length(text())=string-length("ABCDE")]'), ...)

Related

How to avoid redrawing entire tab every time in KnockoutJS?

In this very basic example using Bootstrap Tabs, every time I click on a tab, KnockoutJS redraw the entire tab, is there a way to "save" the state of the tab so next time I click on Messages, KO doesn't have to redraw the entire thing?
It takes ~100ms to draw. (According to Chromium profiler), fps drops to 1fps. It makes the whole thing very unresponsive.
<div>
<ul class="nav nav-tabs" role="tablist">
<li role="presentation">Profile</li>
<li role="presentation">Messages</li>
</ul>
<div class="tab-content">
<div role="tabpanel" class="tab-pane active" id="tab_storage" data-bind="with: BuildingsVM">
<h2 data-bind="i18next: 'AllOurStorage'"></h2>
<div class="row" data-bind="foreach: buildingsByType['RawStorage']">
<div data-bind="template: { name: 'tpl_building' }"></div>
</div>
<div class="row" data-bind="foreach: buildingsByType['RefinedStorage']">
<div data-bind="template: { name: 'tpl_building' }"></div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="messages" data-bind="messages">
<div data-bind="foreach: message">
<span data-bind="text: title"></span>
</div>
</div>
</div>
</div>
Is it possible to keep the state of the DOM so KO doesn't have to redraw the entire tab every time I click on it? (Or an alternative solution)
I'm using the following ko options to avoid unnecessary updates when I array.push(a_lot_of_things):
ko.options.deferUpdates = true;
ko.options.rateLimit = 25;
EDIT:
Tabs are switched using Bootstrap JS library.
$("#MenuTab").on('click','a', function(e) {
e.preventDefault();
var id = $(this).attr('href');
$(this).tab("show");
});
Template for a building 'card':
<script type="text/html" id="tpl_building">
<div class="col-xs-10 col-sm-6 col-md-3">
<div class="card card-raised">
<div class="card-header"><h3 data-bind="text: buildingName"></h3> </div>
<div class="card-content">
<div class="row" data-bind="if: (buildingType == 'RawStorage' || buildingType == 'RefinedStorage')">
<div class="col-xs-4">
Usage: <span data-bind="text: getOccupyingSpace"></span> / <span data-bind="text: TotalCapacity"></span>
</div>
<div class="col-xs-8">
<span data-bind="meter: {value: getOccupyingSpace, max: TotalCapacity}"></span>
</div>
</div>
<div class="row">
<!-- ko foreach: Ressources -->
<div class="col-md-6" data-bind="if: $parent.TotalCapacity">
<span data-bind="text: Name"></span> <span data-bind="text: Qte"></span> (<span class="small" data-bind="text: getOccupyingSpace"></span>)
<!-- ko if: ProductionRate() -->
<span data-bind="text: ProductionRate()"></span>/s
<!-- /ko -->
</div>
<div class="col-md-6" data-bind="if: ProductionRate">
<span data-bind="text: Name"></span> <span data-bind="text: getProductionRatePerSecond()"></span>/s (1 every <span data-bind="text: ProductionRate()/1000"></span> sec)
</div>
<!-- /ko -->
</div>
</div>
<div class="card-footer">
<!-- ko if: initialConstructionGoing -->
<i class="fa fa-exclamation-triangle icon-gradient-warning" class="ko-popover"
data-bind="popover: initialConstructionGoing, popoverOptions: { title: i18nextko.t('UnderConstruction_potitle') }" ></i>
<span data-bind="i18next: 'UnderConstruction'"></span> <span data-bind="text: getBuildProgress"></span>
<span data-bind="meter: {p: getBuildProgress, onComplete: 'remove'}"></span>
<span data-bind="WorkersAssigned().length"></span>/<span data-bind="WorkersBuild"></span>
<!-- /ko -->
</div>
</div>
</div>
</script>
Custom binding for 'meter' (A progress bar):
ko.bindingHandlers.meter = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// This will be called when the binding is first applied to an element
// Set up any initial state, event handlers, etc. here
var progress = ko.unwrap(valueAccessor());
if(progress.value) {
var value = ko.unwrap(progress.value);
var max = ko.unwrap(progress.max);
if(value === 0 && max === 0) {
var percentage = 0;
} else {
var percentage = (value/max)*100;
}
} else {
var percentage = ko.unwrap(progress.p);
}
$progress_tpl = $('<div class="meter"><span style="width: '+percentage+'%"></span></div>');
if(progress.onComplete) {
$progress_tpl.attr('data-oncomplete', progress.onComplete);
}
$(element).append($progress_tpl);
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// This will be called once when the binding is first applied to an element,
// and again whenever any observables/computeds that are accessed change
// Update the DOM element based on the supplied values here.
var progress = ko.unwrap(valueAccessor());
if(progress.value) {
var value = ko.unwrap(progress.value);
var max = ko.unwrap(progress.max);
var percentage = (value/max)*100;
} else {
var percentage = ko.unwrap(progress.p);
}
$(element).find('.meter span').stop().animate({'width': percentage+'%'}, (bindingContext.$root.cf.refreshEvery), 'swing', function() {
if(percentage >= 100) {
if($(element).find(".meter").attr('data-oncomplete') == 'remove') {
$(element).hide(300, function() {
$(element).remove();
ko.cleanNode(element);
});
} else if($(element).find(".meter").attr('data-oncomplete') == 'reset') {
$(element).hide(300, function() {
$(element).remove();
});
} else {
}
}
});
}
};
my KO main VM:
var mainVM = {
Ressources: new RessourcesVM(),
Buildings: new BuildingsVM(),
cf: {
...
}
}
Also modified original tab-content code to reflect the real code (using templates).
I would've made a JSfiddle but for some obscure reasons, it doesn't seem to work properly (new router firmware might be the issue, blocking CDN)à
For a better understanding cotext-wise, you can have a look at my other question: How can I optimize my KnockoutJS pureComputed function?
Looking at the DOM tree in the console, all the elements are still present, even if the tab is not active - maybe something else make the delay.

Javascript to sort elements

I've need to sort some items on the page where the CMS I use doesn't provide for this. I can add some tags dynamically, but I need help here with some Javascript that would put the items in the correct order.
Further at the top of the HTML page I've got an 'event selector', e.g.:
<div class="w-embed">
<div event-selector="weddings"></div>
</div>
This would determine which set of the sorting numbers to use. Each item includes some code with a sort number from each set. w-dyn-item are the elements that need sorting.
<div class="w-dyn-list">
<div class="w-dyn-items">
<div class="w-dyn-item">
<div class="w-embed">
<div sort-event="conference" sort="09"></div>
<div sort-event="exhibition" sort="110"></div>
<div sort-event="wedding" sort="2"></div>
</div>
<div>
Content A
</div>
</div>
<div class="w-dyn-item">
<div class="w-embed">
<div sort-event="conference" sort="06"></div>
<div sort-event="exhibition" sort="60"></div>
<div sort-event="wedding" sort="1"></div>
</div>
<div>
Content B
</div>
</div>
<div class="w-dyn-item">
<div class="w-embed">
<div sort-event="conference" sort="01"></div>
<div sort-event="exhibition" sort="54"></div>
<div sort-event="wedding" sort="3"></div>
</div>
<div>
Content C
</div>
</div>
</div>
</div>
The logic would be: Sort w-dyn-item elements by using the 'sort-event' numbers that correspond to the 'event-selector' (from smallest to largest number).
The site's using jQuery if that's any help.
I've put it into a Fiddle here: https://jsfiddle.net/j2rqze8p
Many thanks for any help.
Here is how you can actually sort the elements with jQuery.
var sortPropertyName = $('.w-embed [event-selector]').attr('event-selector');
var sortAttrValues = {
'weddings': 'wedding',
'exhibitions': 'exhibition',
'conferences': 'conference'
};
var sortAttributeValue = sortAttrValues[sortPropertyName];
if(!sortAttributeValue) {
throw new Error('Unable to sort. Sort attribute value not found.')
}
var attrSelector = '[sort-event="' + sortAttributeValue + '"]';
var $container = $('.w-dyn-items');
var $items = $container.children('.w-dyn-item');
$items.sort(function (item1, item2) {
var item1Value = $(item1).find(attrSelector).attr('sort');
var item2Value = $(item2).find(attrSelector).attr('sort');
return parseInt(item1Value) - parseInt(item2Value);
});
$items.detach().appendTo($container);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="w-dyn-list">
<div class="w-embed">
<div event-selector="weddings"></div>
</div>
<div class="w-dyn-items">
<div class="w-dyn-item">
<div class="w-embed">
<div sort-event="conference" sort="09"></div>
<div sort-event="exhibition" sort="110"></div>
<div sort-event="wedding" sort="2"></div>
</div>
<div>
Content A
</div>
</div>
<div class="w-dyn-item">
<div class="w-embed">
<div sort-event="conference" sort="06"></div>
<div sort-event="exhibition" sort="60"></div>
<div sort-event="wedding" sort="1"></div>
</div>
<div>
Content B
</div>
</div>
<div class="w-dyn-item">
<div class="w-embed">
<div sort-event="conference" sort="01"></div>
<div sort-event="exhibition" sort="54"></div>
<div sort-event="wedding" sort="3"></div>
</div>
<div>
Content C
</div>
</div>
</div>
</div>
There are two moments.
First, as I noticed the value of event-selector is plural whereas sort-event are singular.
I solved this by having a hash to match one to another.
The another thing is that you might need to run this script on 'event-selector' change if you want the sort to by dynamic. But it's basically a matter of a different discussion.

How to separate Kendo Tabstrip tab list and content divs in html?

So, I'm working with kendo in js. I'd like to use the tabstrip, but I'd like the tabs to be contained in a header that is styled and contains some other elements, so I am wondering if there is a way to separate the tab list and their associated divs. I'm looking to have something like this:
<div class="border">
<div class="header">
<div id="tabstriplist">
<ul>
<li id="tab1">First tab</li>
<li id="tab2">Second tab</li>
</ul>
</div>
<div>Some other text</div>
</div>
<div class="content">
<div id="tabcontent1">Stuff</div>
<div id="tabcontent2">Stuff</div>
</div>
</div>
However, it seems like all the example code I can find simply puts the associated divs immediately after the list, so the order of the divs corresponds to the list order. Is there any way to accomplish this sort of thing?
Edit: to clarify, I am looking for a way to display the tabcontent divs outside of the header. I don't really care about where the actual divs are in the html, but rather, where they will appear when they are generated on the page.
Ok, I figured it out. I don't think there is a way to directly attach the content divs to the tabs, but you can achieve the same functionality with the "show" or "select" events for the tabstrip. Here's what I ended up writing, boiled down.
Html:
<div class="border">
<div class="header">
<div id="tabstriplist">
<ul>
<li id="tab1">First tab</li>
<li id="tab2">Second tab</li>
</ul>
<div></div>
<div></div>
</div>
<div>Some other text</div>
</div>
<div class="content">
<div id="tabcontent1">Stuff1</div>
<div id="tabcontent2">Stuff2</div>
</div>
</div>
Js:
$(document).ready(function () {
function onTabSelect(selection) {
var contents = $("#content").children();
var tabNum = selection.contentElement.id.charAt(selection.contentElement.id.length - 1);
for (i = 0; i < contents.length; i++) {
if (tabNum - 1 === i) {
$("#" + contents[i].id).show();
}
else {
$("#" + contents[i].id).hide();
}
}
}
$("#tabstrip").kendoTabStrip({
show: onTabSelect
}).data("kendoTabStrip").select(0);
$("#tabstrip-1").remove();
$("#tabstrip-2").remove();
First solution would be to leave empty divs like:
<div class="border">
<div class="header">
<div id="tabstriplist">
<ul>
<li id="tab1">First tab</li>
<li id="tab2">Second tab</li>
</ul>
<div></div>
<div></div>
</div>
<div>Some other text</div>
</div>
<div class="content">
<div id="tabcontent1">Stuff</div>
<div id="tabcontent2">Stuff</div>
</div>
</div>
And then set tab content whenever you want
$(document).ready(function() {
var tabstrip = $("#tabstriplist").kendoTabStrip().data("kendoTabStrip");
var item = tabstrip.contentElement(0);
//replace the first tab content
$(item).html($("#tabcontent1").html());
});
Or to create the entire tab strip dinamically using:
<div id="tabstrip"></div>
<div class="content">
<div id="tabcontent1">Stuff</div>
<div id="tabcontent2">Stuff2</div>
</div>
And JS to initialize like:
var tabstrip = $("#tabstrip").kendoTabStrip().data("kendoTabStrip");
tabstrip.append({
text: "Firts tab",
content: $("#tabcontent1").html()
});
tabstrip.append({
text: "Second tab",
content: $("#tabcontent2").html()
});

WinJS Repeater Only Binding First Property

I have a list of objects with two properties, and I want my repeater to display each of them. The first property should be inside an <h2> tag, and the second should be in an <h3> tag.
HTML:
<div class="dataColumns" data-win-control="WinJS.UI.Repeater">
<h2 data-win-bind="textContent: Title"></h2>
<h3 data-win-bind="textContent: SomeOtherProperty"></h3>
</div>
JS:
var columnData = new WinJS.Binding.List([
{ Title: 'First Title', SomeOtherProperty: 'First SomeOtherProperty' },
{ Title: '2nd Title', SomeOtherProperty: '2nd SomeOtherProperty' }]);
document.querySelector('.dataColumns').winControl.data = columnData;
Here is the actual output, as seen in the DOM Explorer:
<div class="dataColumns win-repeater win-disposable" data-win-control="WinJS.UI.Repeater">
<h2 class="win-disposable">First Title</h2>
<h2 class="win-disposable">2nd Title</h2>
</div>
Why is only the <h2> shown for each item?
Here is what I would have expected:
<div class="dataColumns win-repeater win-disposable" data-win-control="WinJS.UI.Repeater">
<h2 class="win-disposable">First Title</h2>
<h3 class="win-disposable">First SomeOtherProperty</h3>
<h2 class="win-disposable">2nd Title</h2>
<h3 class="win-disposable">2nd SomeOtherProperty</h3>
</div>
A Repeater template may have only one direct descendant element (like in the case below, a single child div:
<div class="dataColumns" data-win-control="WinJS.UI.Repeater" >
<div>
<h2 data-win-bind="textContent: Title"></h2>
<h3 data-win-bind="textContent: SomeOtherProperty"></h3>
</div>
</div>
Results:
You may also consider using a WinJS.Binding.Template for the contents of the Repeater:
<div class="template" data-win-control="WinJS.Binding.Template">
<div>
<h2 data-win-bind="textContent: Title"></h2>
<h3 data-win-bind="textContent: SomeOtherProperty"></h3>
</div>
</div>
<div class="dataColumns" data-win-control="WinJS.UI.Repeater"
data-win-options="{template: select('.template')}">
</div>

Append to newly created div

I have a question to those jquery/ajax/javascript veterans, im creating a skill calculator for a certain game... everything is working fine up until this:
this is the ajax output:
{"skills":{"skill_id_1":"skill_name_1","skill_id_2":"skill_name_2"}}
this is my ajax:
function setOutput(){
if(httpObject.readyState == 4){
var results = eval('('+httpObject.responseText+')');
if (results['skills']){
$('#skilldiv').empty(); // empty div
$.each(results['skills'], function(key, value)
{
$("<div></div>").appendTo("#skilldiv").attr({class: "skilldesc"});
$("<div> </div>").appendTo(".skilldesc").attr({class: "skillinfo"});
$("<div> </div>").appendTo(".skilldesc").attr({class: "skilltxt"});
});
}
}
}
this is my div:
<div id="wrapper">
<ul>
<li id="skilldiv"></li>
</ul>
</div
so my problem is.. how can i append skillinfo & skilltxt to skilldesc which is just appended to skilldiv...
i tried this:
$.each(results['skills'], function(key, value)
{
$("<div></div>").appendTo("#skilldiv").attr({class: "skilldesc"});
$("<div> </div>").appendTo(".skilldesc").attr({class: "skillinfo"});
$("<div> </div>").appendTo(".skilldesc").attr({class: "skilltxt"});
});
but all results go into the first skilldesc,
im trying to get these:
<div id="wrapper">
<ul>
<li id="skilldiv">
<div class="skilldesc">
<div class="skillinfo">Some Info of skill 1 here</div>
<div class="skilltxt">Name of skill 1 here</div>
</div>
<div class="skilldesc">
<div class="skillinfo">Some Info of skill 2 here</div>
<div class="skilltxt">Name of skill 2 here</div>
</div>
</li>
</ul>
</div>
but i get this instead;
<div id="wrapper">
<ul>
<li id="skilldiv">
<div class="skilldesc">
<div class="skillinfo">Some Info of skill 1 here</div>
<div class="skilltxt">Name of skill 1 here</div>
<div class="skillinfo">Some Info of skill 2 here</div>
<div class="skilltxt">Name of skill 2 here</div>
</div>
<div class="skilldesc"></div>
</li>
</ul>
</div>
any help would be very much appreciated . . . thx
Because .skilldesc selects the first div too.
You can try this,
$.each(results['skills'], function(key, value)
{
$skilldesc = $("<div></div>").attr({class: "skilldesc"});
$("<div> </div>").appendTo($skilldesc).attr({class: "skillinfo"});
$("<div> </div>").appendTo($skilldesc).attr({class: "skilltxt"});
$skilldiv.appendTo("#skilldiv");
});
Also, you should to manipulations in objects in memory (not in dom).

Resources