Night watch change value of textContent or innerHTML - javascript

I have the following markup and I need to be able to change the values of the textContent for these items:
<div class=".react-grid-HeaderCell-sortable">Jamie</div>
<div class=".react-grid-HeaderCell-sortable">Hutber</div>
<div class=".react-grid-HeaderCell-sortable">Stackoverflow</div>
I know how to change values for input fields, but currently don't know how to do text, maybe with a custom command?
For inputs
.setValue('.someSelector', 'anewString')

Array.from(document.getElementsByClassName('react-grid-HeaderCell-sortable'))
.forEach(x => x.innerHTML = 'new text')
Explanation:
document.getElementsByClassName returns an HTMLCollection, which needs to be transformed to an array with Array.from in order to be iterable.
In order to modify text of a div, you need to set the innerHTML attribute. Please note innerHTML can be replaced by textContent, refer to W3C docs for more information on the difference.

As already noted, the only way to do this is to use .execute() and modify DOM from inside the page context.
However, depending on your particular use cases, you may want to use more complex selectors, so I am suggesting to use querySelector() for that.
In addition, you can also create a shortcut for this operation as shown below:
module.exports = {
'Test': function(client) {
client
.url('http://some.url')
.execute(setElementValue, ['table.selector > .child-selector', 'new value'])
.end();
}
};
function setElementValue(selector, value) {
document.querySelector(selector).textContent = value;
}
You might as well create a custom command for that, so you'll be able to reuse it in other tests and make your code look well:
// ./lib/custom-commands/setElementValue.js
exports.command = function(selector, value) {
this.execute(function(selector, value) {
document.querySelector(selector).textContent = value;
}, [selector, value]);
return this;
};
// Test case
client
.url('http://some.url')
.setElementValue('table.selector > .child-selector', 'new value')
.end();
Don't forget to point Nightwatch to the directory where you keep your custom commands, by setting custom_commands_path property of config file.

Related

Polymer v1.0 computed properties not recalculating

I have this:
is: 'paper-filter',
properties:{
filters:{
computed: 'getFilters(data,byArray,obj_filters)'
},
and it is not updating when obj_filters is updated
still not updating ever with arguments are != undefined
Here a video demoing the problem
https://drive.google.com/open?id=0B2sFtTur-E_CSVBKbVdlRzZzTkE
and the jsfiddle
http://jsbin.com/todonohike/edit?html,output
If I just update the obj_filter :
var new_obj_filters = this._setFilter(title, index, by, max, this.obj_filters);
this.obj_filters = new_obj_filters; // no 'computed properties' are fired.
If I clone the Object it fires just fine:
var obj_filters = this._setFilter(title, index, by, max, this.obj_filters);
this.obj_filters = clone(obj_filters); // TODO why clone ??? but fixed!
Your changes to obj_filters need to be made using the Polymer API (see here and here).
For example,
obj_filters[by][title] = true
should be changed to something like
this.set(["obj_filters", by, title], true)
Also have a look at the API description. To find the relevant part, search for "set(".
Edit
I've found two more issues that cause your problem.
First declare your computed property as follows (note the *).
filters:{
computed: 'getFilters(data,byArray,obj_filters.*)'
},
Second, when you call this.set(["obj_filters",by,title], true); for the first time obj_filters.by has not been defined yet and obj_filters.by.title can't be set because the path does not exist. One way around this is to initialise it first.
if (!this.obj_filters[by]) {
this.set(["obj_filters",by], {});
}
this.set(["obj_filters",by,title], true);

Javascript: override native DOM method

I would like to make my website(actually a web app) packaged into single file. I know a easiest way is to replace all path/url inside my code into data-url. But it will make the file hard to read by human. My idea is to use an array to store all data-url and write a function to obtain them by original url(as query string). It can also return the original url if nothing found.
Example:
store = new Array();
store.push({"url":"/img/icon.png","data-url":"dataurl:XXXXX"});
store.push({"url":"/img/icon1.png","data-url":"dataurl:XXXXX"});
function getResource(url) {
var result = url;
for(var row in store){
if (row.url == url) {
result = row.data-url;
}
}
return result;
}
So to use it, you also need a method to change the attribute in HTML element:
function replaceResource(e,attbName) {
e.target.setAttribute(attbName,getResource(e.target.getAttribute(attbName));
}
And in HTML:
<img src="/img/icon.png" onload="replaceResource(event,'src');" />
But I want to be simpler(since it's not good for human to read also). I want to override the DOM method for each type of HTMLElement(img,script etc.) by something like:
HTMLImageElement.prototype.src.callback = function (event) {
e.target.src = getResource(e.target.src);
}
So you don't need to do anything in HTML:
<img src="/img/icon.png" />
However the code for overriding native DOM method above is only my day-dream. I don't know what is correct syntax :'( Anybody can help?
Here is a reference to ADD custom method to native DOM element:
http://www.meekostuff.net/blog/Overriding-DOM-Methods/

TypeError: undefined is not a function jQuery

I'm new to jQuery and I can get it to sometimes work, however, for some reason, when I try to call a function, it gives me the title error, but if I do it in developer tools, it works fine.
http://jsfiddle.net/otanan/pmzzLo3e/#&togetherjs=AezijhfBrj
It seems to work fine when retrieving the classes from the DOM, but not when I call a function such as
.click(function() {});
Here's the code:
var downloads = $(".info"),
className = "info_clicked";
for(var i in downloads)
{
downloads[i].click(function()
{
if(!this.hasClass(className))
this.addClass(className);
else
this.removeClass(className);
});
}
When you access a jQuery collection as an array, it returns the DOM elements, not jQuery objects. You should use .each() rather than for (i in downloads):
downloads.each(function() {
$(this).click(function() {
if (!$(this).hasClass(className)) {
$(this).addClass(className);
} else {
$(this).removeClass(className);
}
});
});
You could also simplify the whole thing to:
downloads.click(function() {
$(this).toggleClass(className);
});
Most jQuery methods automatically iterate over all the elements in a collection if it makes sense to do so (the notable exceptions are methods that return information from the element, like .text() or .val() -- they just use the first element). So you generally only have to iterate explicitly if you need to do different things for each element. This is one of the great conveniences of using jQuery rather than plain JS: you rarely have to write explicit iterations.
I think the issue is that you're attempting to call a jQuery function on an object that is no longer a jQuery object.
For example you're saying $(".info"). Which retrieves a single jQuery object. As soon as you index that object downloads[i] it is no longer a jQuery object, it is a plain HTML element and does not have a click function available.
What you really need to do is get the jQuery object for the indexed item:
var downloads = $(".info"),
className = "info_clicked";
for(var i = 0; i < downloads.length; i++)
{
$(downloads[i]).click(function()
{
if(!this.hasClass(className))
this.addClass(className);
else
this.removeClass(className);
});
}
try it:
$(downloads[i]).click(function(){ //...

JavaScript/DOM - Help removing my inline JavaScript

Being new to JavaScript I have not been able to come up with a solution to this issue.
I want each "Add to Cart" button to invoke the same function "AddtoCart". I have achieved this but at the cost of inline JavaScript - something I would like to avoid.
onclick=
"AddToCart(document.getElementById('toy_title1').innerHTML,document.getElementById('toy_quantity1').value,document.getElementById('toy_price1').innerHTML)
So how would I achieve including this as part of the external JavaScript file, bearing in mind I have to be able to apply this to all 4 unique items
You could change your function that way:
function AddToCart(toyId) {
var title = document.getElementById('toy_title'+toyId).innerHTML;
var quantity = document.getElementById('toy_quantity'+toyId).value;
var price = document.getElementById('toy_price')+toyId).innerHTML
}
Then on each button you just pass the toy's ID
Just be carefull about sensitive data like price, leaving it on Javascript(I'm supposing you will send it to your back-end after this) is dangerous, it could be easily manipulated.
But if your intention is just a test or something like that, its ok.
EDIT:
to call your this function you would do something like that:
onclick="AddToCart(1)"
Where 1 is your toy's ID, you should change it to 2,3... depending on your toy.
then you should read more about addEventListener(standard) and attachEvent(IE)
//assume element means the button
//you can use getElementsByTagName, getElementsByClassName, querySelectorAll etc.
//to fetch your elements
//DRY, store the operation in a function so it's reusabe and not written twice
function thisFunction(){
AddToCart(document.getElementById('toy_title1').innerHTML,
document.getElementById('toy_quantity1').value,
document.getElementById('toy_price1').innerHTML)
}
if(element.addEventListener){ //check if the standard is supported
element.addEventListener('click',function(){ //use it to add the handler
thisFunction();
});
} else {
element.attachEvent('onclick',function(){ //else, we use IE's version
thisFunction();
}, false);
}

jquery Syncing dom element values

I have a DOM element like this:
<div id='master-value'>Apples</div>
I have many other elements elsewhere on the page that I need to sync with the 'master-value'.
<div class='fruit-value' data-reference='master-value'>Apples</div>
<div class='some-fruit' data-reference='master-value'>Apples</div>
When I change the value of the 'master-value', I want all the synced elements to update with it.
$('#master-value').text('Pears');
Should affect:
<div class='fruit-value' data-reference='master-value'>Pears</div>
<div class='some-fruit' data-reference='master-value'>Pears</div>
What I don't want, is on every change of 'master-value' to have to search through all the elements in order to find the synced elements in order to change them. I think that's quite slow when there are many elements that needs to be searched through.
There should be some way for the child values to be pre-bound to the master value so that the selection goes quickly.
$('.fruit-value, .some-fruit').sync('#master-value');
I have some ideas, for instance: I can create an array of preselected synced objects, bind a custom event on the master value and run that event whenever I change the value. The event would go through the array to update all the child elements.
I'm sure there's a better way of doing it though...
Thanks!
You can store the selector once, like this:
var elements = $('.fruit-value, .some-fruit'); //do this once
elements.text($("#master-value").text()); //when you want to sync
The elements variable/jQuery object will keep an array of references to DOM elements so it won't be traversing to find them each time.
wouldn't it be easier to give them all the same class?
So you coud do
$('.fruit').text('Pears')
If you're looking for plugin type of functionality, try this:
When setting up, it takes an object with one property syncWith to set up the elements it should sync with.
When setting the text, it will set the text for the master and the synced elements.
Try it out: http://jsfiddle.net/GH33J/
Just a first attempt. There would be room for improvement if (for example) the master was more than one element. There should be a global reference to all the elements to synchronize and an option to tell if the masters should be synced too.
$.fn.sync = function(arg) {
// if arg plain object, we are doing an initial setup
if ($.isPlainObject(arg)) {
return this.each(function() {
$.data(this, 'syncWith', $(arg.syncWith));
});
// if arg is jQuery object, we are adding new items
} else if (arg.jquery) {
return this.each(function() {
var $set = $.data(this, 'syncWith');
$.each(arg, function() {
$set.push(this);
});
});
console.log(this.data('syncWith'));
// otherwise assume we have a string, and are syncing a new value
} else {
return this.each(function() {
$(this).text(arg);
$.data(this, 'syncWith').text(arg);
});
}
};
// Set up the sync
$('#master-value').sync({
syncWith: '.fruit-value,.some-fruit'
});
var $new = $('<div class="fruit-value">Apples</div>').appendTo('body');
// Pass a jQuery object containing newly created element(s) to add to the set
$('#master-value').sync($new);
// Activate a sync
$('#master-value').sync("pears");​
OK here we go:
This is the official data linking plugin from Microsoft. It's now being supported by the jQuery Core team, so we know it's good. :)
http://weblogs.asp.net/scottgu/archive/2010/05/07/jquery-templates-and-data-linking-and-microsoft-contributing-to-jquery.aspx
http://blog.jquery.com/2010/10/04/new-official-jquery-plugins-provide-templating-data-linking-and-globalization/

Categories