append xpath to element - javascript

I use a page object with an element like
myButton: {
selector: '//button[#ng-click="$clientCtrl.add()"]',
locateStrategy: 'xpath'
}
While my test is running, I expect my button to be disabled.
I could add a new element like this:
myButtonDisabled: {
selector: '//button[#ng-click="$clientCtrl.add()"][#disabled="disabled"]',
locateStrategy: 'xpath'
}
But I have many buttons, so I would get a huge list of enabled and disabled buttons.
So instead I'd like to write a command inside my page object like this:
var myCommands = {
assertButtonDisabled: function(mybutton) {
this.api
.pause(500)
.useXpath()
.expect.element(mybutton + '[#disabled="disabled"]', 5000).to.be.present;
return this;
}
};
which I call inside my test via
myPageObject.assertButtonDisabled('#myButton');
Well, this fails because I cannot concatenate an element like #myButton with an additional xpath ([#disabled="disabled"]).
So what can I do instead of adding multiple new elements?

From maintainability perspective it would be much easier to maintain things this way:
myButton: {
selector: '//button[#ng-click="$clientCtrl.add()"]',
locateStrategy: 'xpath'
}
myButtonDisabled: {
selector: '//button[#ng-click="$clientCtrl.add()"][#disabled="disabled"]',
locateStrategy: 'xpath'
}
Just use proper naming for those elements.

Related

How to remove particular class name from an selected element in VueJs?

This is what my vuejs methods looks like. Here in the changeRoute function I can change class name by e.target.className = 'clicked'; But when I try to remove that class name from other elements I cant do it by pre.removeClass('clicked'); How can I accomplish this ?
<script>
export default {
components: {
},
data() {
return {
}
},
methods: {
changeRoute(e, route) {
var pre = this.$el.querySelector('.clicked');
if(pre) {
// pre.removeClass('clicked');
}
this.$router.push({
name: route
});
e.target.className = 'clicked';
}
},
mounted() {
this.$nextTick(() => {
})
}
}
</script>
Also how can push class name rather than replace all by e.target.className = 'clicked';
You can use classList.add and classList.remove for this.
But it seems like you want to style link depending on the current route which can be done with vue-router as it adds class on the links matching the current route.
Vue-router active class
If the selected element is part of a li list and class should be added (and consequently removed from previous clicked li), here's a good solution by Herteby on the VueJS Forum
https://forum.vuejs.org/t/how-to-add-active-class-to-element/28108/2
Use actual Vue to accomplish this task.
I could explain here how but the Vue page does it quite well.
https://v2.vuejs.org/v2/guide/class-and-style.html

Add most of a id on JQuery

How can to add a new id in my $(), in this code I added an only id, but I need add other more.
Thanks!
$('#editable-detail').editableTableWidget();
$('#editable-detail td.uneditable').on('change', function(evt, newValue) {
return false;
});
You can add a comma-delimited list of selectors:
$('#editable-detail, #editable-detail2').editableTableWidget();
If you want to select multiple ID's using jQuery, just seperate them with a comma.
$('#editable-detail, #otherID').editableTableWidget();
$('#editable-detail td.uneditable').on('change', function(evt, newValue) {
return false;
});
You can see it in action here: JSFiddle
You can select many elements via multiple selectors using a comma:
$('#editable-detail1, #editable-detail2').editableTableWidget();
Or you could put a class, say editable-table-widget, on the ones you want to do this to, and select by class:
$('.editable-table-widget').editableTableWidget();
This leaves the rest of your code to deal with, since I imagine you'd want the same change event bound to the child elements. (NOTE: you bound a change event to a td element...that's quite odd...)
Your original code:
$('#editable-detail').editableTableWidget();
$('#editable-detail td.uneditable').on('change', function(evt, newValue) {
return false;
});
Would be better written as one of the two following:
$('#editable-detail')
.editableTableWidget()
.find('td.uneditable')
.on('change', function(evt, newValue) {
return false;
});
OR:
var editableDetail = $('#editable-detail').editableTableWidget();
editableDetail.find('td.uneditable').on('change', function(evt, newValue) {
return false;
});
This change is beneficial because you aren't re-querying the DOM for the element with ID of editable-detail.
With that change in place, you could apply the lesson about using commas or a class with this change, and use the following for your whole code sample replacement:
$('.editable-table-widget')
.editableTableWidget()
.find('td.uneditable')
.on('change', function(evt, newValue) {
return false;
});
you either do like:
$('#editable-detail, #editable-detail2, #editable-detail3').editableTableWidget();
or (Recommended):
use classes instead of id (you should give them all the same class):
$('.editable-detail').editableTableWidget();
you can add comma to seperate them
$('#editable-detail, #otherid').editableTableWidget();

namespaced toggling for CSS classes

jquery has this function
http://api.jquery.com/toggleClass/
but it does not allow name-spacing. With name-spacing you can toggle between multiple classes as if they were radio buttons.
for example
toggleClass(el, 'ns1', 'test')
will check ns1 and will remove any other class with this namespace and replace it with the class test
I wrote this library function to keep my application code.
One mistake I made was I prefixed the class with the namespace rather then make it a separate argument.
Here is my in progress use of a custom toggleClass with name-spacing:
// apply event listeners to the input elements
applyEL: function (input_element, label_element) {
var self = this;
self.J.input = $(input_element);
self.J.input.on("blur", function () {
if (input_element.value === '') {
$A.toggleClass(input_element, self.S.toggle_border_show);
$A.toggleClass(label_element, self.S.toggle_label_show);
$A.expandFont(label_element, 'up', self.S.speed);
}
}, false);
self.J.input.on("focus", function () {
if (input_element.value === '') {
$A.toggleClass(input_element, self.S.toggle_border_obscure);
$A.toggleClass(label_element, self.S.toggle_label_obscure);
}
}, false);
self.J.input.on("paste keypress drop", function () {
$A.setTimeout(function (label_element, input_element) {
$A.toggleClass(label_element, this.S.toggle_label_hide);
$A.toggleClass(input_element, this.S.toggle_border_hide);
}, 0);
}, false);
}
Is there a better way to do this, other from what I mentioned.
Better way would be, rather add a class to html or body element, which would act as a name space, much like common theme switchers?
And if you need a local name space for a specific element you would reference the parent element.
And boom, you have name spaces!

A smart toggle class in jQuery

I am trying to implement a script to set different class name on a specific element…
Let's suppose the dom looks like this:
<body class='pre-existing-class-name'>
If I make
smartToogle('body', 'new-class');
// the dom should look like this
// <body class='pre-existing-class-name new-class'>
smartToogle('body', 'new-class-2');
// the dom should look like this
// <body class='pre-existing-class-name new-class-2'>
I did the following code but it does not work:
var smartToogle = function (element, newClassName) {
var oldClassName;
var $element = $(element);
$element.addClass(newClassName);
if (oldClassName !== newClassName) {
$element.removeClass(oldClassName);
}
oldClassName = newClassName;
};
Requirements:
1) I am using query
2) I would like to pass just one class name, the new one.
Solution:
The following code works but I do not like it because it uses global variable.
Any hint to fix it?
function myToggle(newClassName) {
if (window.oldClassName) {
$('body').toggleClass(window.oldClassName);
}
window.oldClassName = newClassName;
$('body').toggleClass(newClassName);
}
You can use data attribute for the element, that is accessible using
$(element).data(attrib_name)
Just a small change is required in your method
function myToggle(newClassName) {
if (window.oldClassName) {
$('body').toggleClass(window.oldClassName);
}
window.oldClassName = newClassName;
$('body').toggleClass(newClassName);
}
can be replaced with
function myToggle(element, newClassName) {
if ($(element).data('oldClassName')) {
$(element).toggleClass($(element).data('oldClassName'));
}
$(element).data('oldClassName', newClassName)
$(element).toggleClass(newClassName);
}
Hope this solves it for you.
Update:
There is one thing you need to understand.
If you want two different behaviors you don't need 2 different classes for the change in behavior.
One is enough, because you can change the behavior based on weither the class is on or off.
Let's say I want my element to have a red hover event in one way.
And want it to have a blue hover event the other way with CSS.
Then this is the way to go:
$('#toggle').click(function(){
$('.normal').each(function(){
$(this).toggleClass('active');
});
});
JSFiddle Demo
Here we use a button to toggle all the divs and change their CSS behavior, looks easy now right?
However if you need to toggle Javascript/jQuery events as well this won't do. In that case you will need to use 3 other methods to manage this; .on(), .off(), and .hasClass().
$('#toggle').click(function(){
$('.normal').each(function(){
if($(this).hasClass('active')){
$(this).off('click');
} else {
$(this).on('click', function(){
alert('You are clicking on an active div.');
});
}
$(this).toggleClass('active');
});
});
JSFiddle Demo 2
As you can see we have added an if statement. If the element has the .active class we turn .off() the .click(). And if there isn't an active class we turn the .click() .on(). Under the if statement we always toggle the .active class. So this doesn't have to be placed inside the if statement.
I hope this clears everything up for you, good luck!
Old Answer:
It is better to use .toggleClass() here.
Use a first class on the element for the default properties and a second like .active for example for the interaction.
Also, using a .on('click', function(){}) bind will make you able to add interaction that will be bound instantly once the element is toggled.
Here's a fiddle: http://jsfiddle.net/NCwmF/2/
I little jQuery plugin for that. Removes the current smart class (if any) and adds the new smart class. If called without parameter className the current smart class gets only removed.
$.fn.smartToggle = function (className) {
var dataId = 'smartToggle';
return this.each(function () {
var $el = $(this);
$el
.removeClass($el.data(dataId) || '')
.addClass(className)
.data(dataId, className);
});
};
​use it like every other jQuery method:
$('body').smartToggle('myClass');
NEW, SIMPLER ANSWER
Works similar to before, with 2 additions: 1.) works if there is no class initially and 2.) works if other functions change the elements class in between calls. I also changed the function name so it doesn't interfere with jQuerys native toggleClass.
$.fn.fancyToggleClass = function(new_class) {
return this.each(function() {
// get the last class this function added (if exists) or false (if not)
var $this = $(this),
toggled_class = $this.data('toggled-class') || false;
// if we dont have an original class, then set it based on current class
if (toggled_class) {
$this.removeClass(toggled_class);
}
// add new class and store as data,
// which we check for next time function is called
$this.addClass(new_class).data('toggled-class', new_class);
// alert the class, just as a check to make sure everything worked!
// remove this for production, or switch to console.log
alert('element class: ' + $this.attr('class'));
});
}
updated fiddle: http://jsfiddle.net/facultymatt/xSvFC/3/
OLD ANSWER
I would suggest storing the original class in the elements data attribute. Then, your function can check if this data is set, and if so clear the elements class adding the original class from the elements data and also the new class you passed in the function.
If data is not set, the function will store the current class as data the first time it runs.
Check out this fiddle for a working example with comments: http://jsfiddle.net/facultymatt/xSvFC/
here is the code. It's a jquery function so it can be called on any element (and is chainable too!)
$.fn.toggleClass = function(new_class) {
return this.each(function() {
// cache selector for this
$this = $(this);
// get original class (if exists) or false (if not)
var original_class = $this.data('original-class') || false;
// if we dont have an original class, then set it based on current class
if (!original_class) {
original_class = $this.attr('class');
$this.data('original-class', original_class);
// we do have an original class, so we know user is now trying to add class
// here we clear the class, add the original class, and add the new class
} else {
// assign the original class, and new class,
// and a space to keep the classes from becoming one
$this.attr('class', original_class + ' ' + new_class);
}
// alert the class, just as a check to make sure everything worked!
// remove this for production, or switch to console.log
alert('element class: ' + $this.attr('class'));
});
}
Hope this helps!
To avoid a global variable you can use data attribute as #ankur writes. Here is a working solution for your problem:
function myToggle(element, newClassName) {
if (!$(element).data('baseclassname')) {
$(element).data('baseclassname', $(element).attr('class'));
}
$(element)
.attr('class', $(element).data('baseclassname'))
.addClass(newClassName);
}
Does this do your job?
var smartToogle = function (element, preExistingClassName, newClassName) {
$(element)[0].className = preExistingClassName + ' ' + newClassName;
};
Just use hasClass. But you'll have to tell the function what both classes are:
function smartToggle(element, class1, class2) {
var $element = $(element);
if ($element.hasClass(class1)) {
$element.removeClass(class1);
$element.addClass(class2);
}
else {
$element.removeClass(class2);
$element.addClass(class1);
}
}
$(function(){
var smartToggle = function (element, newClassName) {
var elementClasses = element.attr('class');
element.addClass(newClassName);
// check if there is more than one class on the element
if(elementClasses .indexOf(' ') >= 0){
var oldClassNames = elementClasses.split(" ");
if (oldClassNames[oldClassNames.length - 1] !== newClassName) {
element.removeClass(oldClassNames[oldClassNames.length - 1]);
}
}
};
smartToggle($('.test'), 'newclass');
smartToggle($('.test'), 'newclass2');
});
Demo - http://jsfiddle.net/Q9A8N/ (look at the console to see what it is doing on each pass)
That should do what you want but as #T.J. Crowder said it is rather fragile and assumes that the class you want to remove is the last one on the element.
As an answer to your question, I would go with ankur's answer
As a follow-up to Sem's answer, regarding the handling of jQuery events :
you can use the on function to handle any jquery event from a parent node, based on a live filter :
function myToggle(element, newClassName) {
if ($(element).data('oldClassName')) {
$(element).toggleClass($(element).data('oldClassName'));
}
$(element).data('oldClassName', newClassName);
$(element).toggleClass(newClassName);
}
//event delegation : 'on' is called on the $('.divContainer') node, but we handle
//clicks on '.divItm' items, depending on their current class
$('.divContainer')
.on('click', '.divItm.plain', function(){ myToggle( this, 'red' ); })
.on('click', '.divItm.red', function(){ myToggle( this, 'blue' ); })
.on('click', '.divItm.blue', function(){ myToggle( this, 'plain' ); });
//initialize each item with the 'plain' class
myToggle( $('.divItm'), 'plain' );
Here is the jsFiddle.
You will note that the function called each time you click on an item depends on its "live" class, and that you don't need to manually enable/disable click handlers each time an item changes class.
You can learn more details from the documentation page.
var smartToogle = function (element, newClass) {
var $element = $(element),
currentClass = $element.data('toggle-class');
if (currentClass != newClass) $element.data('toggle-class',newClass).removeClass(currentClass || '');
$element.toggleClass(newClass);
};
or the other variant:
$.fn.smartToogle = function (newClass) {
currentClass = this.data('toggle-class');
if (currentClass != newClass) this.data('toggle-class',newClass).removeClass(currentClass || '');
this.toggleClass(newClass);
};
In this implementation you'll have to keep the a reference to this instance of fancytoggle.
var fancytoggle = function(el, oldClass){
// create a function scope so we'll have a reference to oldClass
return function(newClass) {
// toggle the old class and the new class
$(el).toggleClass(oldClass+ ' ' + newClass);
// update the new class to be the old class
oldClass = newClass;
};
};
for your example the code would look something like.
var bodytoggle = fancytoggle('body', 'pre-existing-class-name');
bodytoggle('new-class');
// 'new-class' replaces 'pre-existing-class-name'
bodytoggle('new-class-2');
// 'new-class-2' replaces 'new-class'
to see it in action refer to http://jsfiddle.net/aaf2L/6/

How to set an event handler in a Django form input field

How to set a JavaScript function as handler in the event onclick in a given field of a Django Form. Is this possible?
Any clue would be appreciated.
What I do for that is :
class MyForm(forms.Form):
stuff = forms.ChoiceField(
[('a','A'),('b','B')],
widget = forms.Select(attrs = {
'onclick' : "alert('foo !');",
}
)
I would recommend looking at using a JavaScript library such as jQuery. Here is the link to binding the click event in jQuery. Since Django names all of the input fields you can connect my_field_name in your Django form to the click event like so:
$("form input[name='my_field_name']").click(function () {
// Handle the click event here
});
If you are looking for automating this process, you can create a custom widget for the field. In the widget class you will define a render method in such a way that it will also return the event binding code.
class CustomWidget(forms.TextInput):
class Media:
#js here
js = ('js/my_js.js',)
def render(self, name, value, attrs = None)
output = super(CustomWidget, self).render(name, value, attrs)
#do what you want to do here
output += ...
return output
Using YUI, you can do that like this:
YAHOO.util.Event.addListener(id_myField, "click", myClickEventHandler, myOptionalData);
See also YUI 2: Event Utility
I prefer using a JavaScript library, as this keeps browser-side JS code nicely separated from server-side Django.
The Ghislain Leveque answer works to me, but in django 1.9 I did it like this:
class myForm(forms.ModelForm):
class Meta:
model = USER
fields = ["password", "idCity"]
widgets = {
"password": forms.PasswordInput(),
"idCity": forms.Select(attrs={'onchange': "alert('foo !');"})
}
I took JQuery approach using Ghislain Leveque's answer. This way I was able to trigger the event handler on page load.
class MyForm(forms.Form):
stuff = forms.ChoiceField(
[('a','A'),('b','B')],
widget = forms.Select(attrs = {
'widget_to_bind' : "True",
}
)
In my javascript:
function myFunction() {
var source = $(this); // Widget element
}
$('[widget_to_bind=True]').change(myFunction);
$('[widget_to_bind=True]').trigger("change");
Variation on a theme:
class CategoriesForm(forms.Form):
stuff = forms.MultipleChoiceField(
choices=([(pt.id, pt.name) for pt in PaymentType.objects.order_by('name')]),
widget=forms.SelectMultiple(attrs={'size': '20', 'class': 'mc_payment_type'})
)
JQuery:
<script language="javascript">
$('.mc_payment_type').change(function () {
console.log('payment type selected: ' + $(this).find('option:selected').val());
</script>

Categories

Resources