Protractor scrollleft but class have multiple instance - protractor

I have an element with class="objbox" but this attribute have multiple instances.
The current code that I use for scrolling is browser.executeScript('$(".objbox").scrollLeft(' + strPixels + ')'); but since there are multiple instances, it seems like it is getting the first instance and scroll was not successfully done to the target element.
I am wondering if it is possible to include the parent element on my code, or if there is a different work around.
<div class="dhxgrid2-wrapper">
<div class="dhtmlxgrid-container gridbox">
<div class="objbox">
...
</div>
</div>
</div>

It's possible.
What you need to do is the following
// Define the elementfinder of your parent, pick option A or B
const elementFinderWithParentA = $('.dhtmlxgrid-container .objbox');
// Or
const elementFinderWithParentB = $('.dhtmlxgrid-container').$('.objbox');
// The amount to scroll
const scrollLeft = 50;
browser.executeScript('arguments[0].scrollLeft = arguments[1];', elementFinderWithParentA, scrollLeft);
// Or making it more readable, make a function for the scrolling
// and pass it to the browser.executeScript
function scrollToLeft(element, scrollAmount) {
element.scrollLeft = scrollAmount;
}
browser.executeScript(scrollToLeft, elementFinderWithParentA, scrollLeft);
Hope it helps

Related

binding event listeners to ES6 objects [duplicate]

Do getElementsByClassName (and similar functions like getElementsByTagName and querySelectorAll) work the same as getElementById or do they return an array of elements?
The reason I ask is because I am trying to change the style of all elements using getElementsByClassName. See below.
//doesn't work
document.getElementsByClassName('myElement').style.size = '100px';
//works
document.getElementById('myIdElement').style.size = '100px';
Your getElementById code works since IDs have to be unique and thus the function always returns exactly one element (or null if none was found).
However, the methods
getElementsByClassName,
getElementsByName,
getElementsByTagName, and
getElementsByTagNameNS
return an iterable collection of elements.
The method names provide the hint: getElement implies singular, whereas getElements implies plural.
The method querySelector also returns a single element, and querySelectorAll returns an iterable collection.
The iterable collection can either be a NodeList or an HTMLCollection.
getElementsByName and querySelectorAll are both specified to return a NodeList; the other getElementsBy* methods are specified to return an HTMLCollection, but please note that some browser versions implement this differently.
Both of these collection types don’t offer the same properties that Elements, Nodes, or similar types offer; that’s why reading style off of document.getElements…(…) fails.
In other words: a NodeList or an HTMLCollection doesn’t have a style; only an Element has a style.
These “array-like” collections are lists that contain zero or more elements, which you need to iterate over, in order to access them.
While you can iterate over them similarly to an array, note that they are different from Arrays.
In modern browsers, you can convert these iterables to a proper Array with Array.from; then you can use forEach and other Array methods, e.g. iteration methods:
Array.from(document.getElementsByClassName("myElement"))
.forEach((element) => element.style.size = "100px");
In old browsers that don’t support Array.from or the iteration methods, you can still use Array.prototype.slice.call.
Then you can iterate over it like you would with a real array:
var elements = Array.prototype.slice
.call(document.getElementsByClassName("myElement"));
for(var i = 0; i < elements.length; ++i){
elements[i].style.size = "100px";
}
You can also iterate over the NodeList or HTMLCollection itself, but be aware that in most circumstances, these collections are live (MDN docs, DOM spec), i.e. they are updated as the DOM changes.
So if you insert or remove elements as you loop, make sure to not accidentally skip over some elements or create an infinite loop.
MDN documentation should always note if a method returns a live collection or a static one.
For example, a NodeList offers some iteration methods such as forEach in modern browsers:
document.querySelectorAll(".myElement")
.forEach((element) => element.style.size = "100px");
A simple for loop can also be used:
var elements = document.getElementsByClassName("myElement");
for(var i = 0; i < elements.length; ++i){
elements[i].style.size = "100px";
}
Aside: .childNodes yields a live NodeList and .children yields a live HTMLCollection, so these two getters also need to be handled carefully.
There are some libraries like jQuery which make DOM querying a bit shorter and create a layer of abstraction over “one element” and “a collection of elements”:
$(".myElement").css("size", "100px");
You are using a array as an object, the difference between getElementbyId and
getElementsByClassName is that:
getElementbyId will return an Element object or null if no element with the ID is found
getElementsByClassName will return a live HTMLCollection, possibly of length 0 if no matching elements are found
getElementsByClassName
The getElementsByClassName(classNames) method takes a string that
contains an unordered set of unique space-separated tokens
representing classes. When called, the method must return a live
NodeList object containing all the elements in the document that
have all the classes specified in that argument, having obtained the
classes by splitting a string on spaces. If there are no tokens
specified in the argument, then the method must return an empty
NodeList.
https://www.w3.org/TR/2008/WD-html5-20080610/dom.html#getelementsbyclassname
getElementById
The getElementById() method accesses the first element with the specified id.
https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById
in your code the lines:
1- document.getElementsByClassName('myElement').style.size = '100px';
will NOT work as expected, because the getElementByClassName will return an array, and the array will NOT have the style property, you can access each element by iterating through them.
That's why the function getElementById worked for you, this function will return the direct object. Therefore you will be able to access the style property.
ES6 provides Array.from() method, which creates a new Array instance from an array-like or iterable object.
let boxes = document.getElementsByClassName('box');
setTimeout(() => {
Array.from(boxes).forEach(v => v.style.background = 'green');
console.log(Array.from(boxes));
}, 500);
.box {
width: 50px;
height: 50px;
margin: 5px;
background: blue;
display: inline-block;
}
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
As you can see inside the code snippet, after using Array.from() function you are then able to manipulate over each element.
The same solution using **`jQuery`**.
$('.box').css({'background':'green'});
.box {
width: 50px;
height: 50px;
margin: 5px;
background: blue;
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
<div class='box'></div>
The following description is taken from this page:
The getElementsByClassName() method returns a collection of all elements in the document with the specified class name, as a NodeList object.
The NodeList object represents a collection of nodes. The nodes can be
accessed by index numbers. The index starts at 0.
Tip: You can use the length property of the NodeList object to determine the number of elements with a specified class name, then you can loop through all elements and extract the info you want.
So, as a parameter getElementsByClassName would accept a class name.
If this is your HTML body:
<div id="first" class="menuItem"></div>
<div id="second" class="menuItem"></div>
<div id="third" class="menuItem"></div>
<div id="footer"></div>
then var menuItems = document.getElementsByClassName('menuItem') would return a collection (not an array) of the 3 upper <div>s, as they match the given class name.
You can then iterate over this nodes (<div>s in this case) collection with:
for (var menuItemIndex = 0 ; menuItemIndex < menuItems.length ; menuItemIndex ++) {
var currentMenuItem = menuItems[menuItemIndex];
// do stuff with currentMenuItem as a node.
}
Please refer to this post for more on differences between elements and nodes.
In Other Words
document.querySelector() selects only the first one element of the specified selector. So it doesn't spit out an array, it's a single value. Similar to document.getElementById() which fetches ID-elements only, since IDs have to be unique.
document.querySelectorAll() selects all elements with the specified selector and returns them in an array. Similar to document.getElementsByClassName() for classes and document.getElementsByTagName() tags only.
Why use querySelector?
It's used merely for the sole purpose of ease and brevity.
Why use getElement/sBy?*
Faster performance.
Why this performance difference?
Both ways of selection has the purpose of creating a NodeList for further use.
querySelectors generates a static NodeList with the selectors thus it must be first created from scratch.
getElement/sBy* immediately adapts the existing live NodeList of the current DOM.
So, when to use which method it's up to you/your project/your device.
Infos
Demo of all methods
NodeList Documentation
Performance Test
You could get a single element by running
document.querySelector('.myElement').style.size = '100px';
but it's going to work for the first element with class .myElement.
If you would like apply this for all elements with the class I suggest you to use
document.querySelectorAll('.myElement').forEach(function(element) {
element.style.size = '100px';
});
It returns Array-like list.
You make that an Array as example
var el = getElementsByClassName("elem");
el = Array.prototype.slice.call(el); //this line
el[0].appendChild(otherElem);
/*
* To hide all elements with the same class,
* use looping to reach each element with that class.
* In this case, looping is done recursively
*/
const hideAll = (className, i=0) => {
if(!document.getElementsByClassName(className)[i]){ //exits the loop when element of that id does not exist
return;
}
document.getElementsByClassName(className)[i].style.visibility = 'hidden'; //hide element
return hideAll(className, i+1) //loop for the next element
}
hideAll('appBanner') //the function call requires the class name
With any browser supporting ES5+ (any browser basically above IE8) you can use the Array.prototype.forEach method.
Array.prototype.forEach.call(document.getElementsByClassName('answer'), function(el) {
el.style.color= 'red';
});
caniuse source
So I was told that this is a duplicate from my question and I should delete mine, which I will do so I can keep the forum clean and keep the right to make questions.
As I think mine and this question are really different I will point out the answer to mine, so I will complete the knowledge in this page and the information will not be lost.
Question
I have a code in the snippet that has a document.getElementsByClassName("close")[0], what the [0] is doing?
I never seen a square brackets being used in getElementsByClassName for what purpose is it used for?
Also, how can I convert it to jQuery?
Answer
The code in the snippet has a [0] it is actually being used as a array and as it is a 0 it is referring to the first time the appointed class is being used.
Same thing above.
I couldn't really do it and no one answered it. In the part of the code that is refering to event. target I can not use $("#myModal") instead of document.getElementById("myModal"), I think they should equivalent, but in this case the jQuery form substituting the standard one will not result in the desired effect.
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
var modal = document.getElementById("myModal");
var btn = document.getElementById("myBtn");
var span = document.getElementsByClassName("close")[0];
btn.onclick = function() {
modal.style.display = "block";
}
span.onclick = function() {
modal.style.display = "none";
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
body {font-family: Arial, Helvetica, sans-serif;}
.modal {
display: none;
position: fixed;
z-index: 1;
padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
.close {
color: #aaaaaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
<h2>Modal </h2>
<button id="myBtn">Open Modal</button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">×</span>
<p>Some text in the Modal..</p>
</div>
</div>
update
It seems I can't really delete mine question and people are unsatisfied with it, I really don't know what I should do.
Super old school solution:
[].forEach.call(document.getElementsByClassName('myClass'), function (el) {
el.style.size = '100px';
});
An answer for Drenzii's specific case...
You could make a function that will work for any of the word elements and pass in the number of the one you want to transform, like:
// Binds `wordButtons` to an (array-like) HTMLCollection of buttons
const wordButtons = document.getElementsByClassName("word");
// Applies the `slantWord` function to the first word button
slantWord(1);
// Defines the `slantWord` function
function slantWord(wordNumber) {
const index = wordNumber - 1; // Collection index is zero-based
wordButtons[index].style.transform = "rotate(7deg)"; // Transforms the specified button
}
<div class="wordGameContainer">
<button class="word word1">WORD 1</button>
<button class="word word2">WORD 2</button>
<button class="word word3">WORD 3</button>
<button class="word word4">WORD 4</button>
</div>
<div>
<button onclick="moveWord()" class="playButton">PLAY</button>
</div>

Is there a way/workaround to have the slot principle in hyperHTML without using Shadow DOM?

I like the simplicity of hyperHtml and lit-html that use 'Tagged Template Literals' to only update the 'variable parts' of the template. Simple javascript and no need for virtual DOM code and the recommended immutable state.
I would like to try using custom elements with hyperHtml as simple as possible
with support of the <slot/> principle in the templates, but without Shadow DOM. If I understand it right, slots are only possible with Shadow DOM?
Is there a way or workaround to have the <slot/> principle in hyperHTML without using Shadow DOM?
<my-popup>
<h1>Title</h1>
<my-button>Close<my-button>
</my-popup>
Although there are benefits, some reasons I prefer not to use Shadow DOM:
I want to see if I can convert my existing SPA: all required CSS styling lives now in SASS files and is compiled to 1 CSS file. Using global CSS inside Shadow DOM components is not easily possible and I prefer not to unravel the SASS (now)
Shadow DOM has some performance cost
I don't want the large Shadow DOM polyfill to have slots (webcomponents-lite.js: 84KB - unminified)
Let me start describing what are slots and what problem these solve.
Just Parked Data
Having slots in your layout is the HTML attempt to let you park some data within the layout, and address it later on through JavaScript.
You don't even need Shadow DOM to use slots, you just need a template with named slots that will put values in place.
<user-data>
<img src="..." slot="avatar">
<span slot="nick-name">...</span>
<span slot="full-name">...</span>
</user-data>
Can you spot the difference between that component and the following JavaScript ?
const userData = {
avatar: '...',
nickName: '...',
fullName: '...'
};
In other words, with a function like the following one we can already convert slots into useful data addressed by properties.
function slotsAsData(parent) {
const data = {};
parent.querySelectorAll('[slot]').forEach(el => {
// convert 'nick-name' into 'nickName' for easy JS access
// set the *DOM node* as data property value
data[el.getAttribute('slot').replace(
/-(\w)/g,
($0, $1) => $1.toUpperCase())
] = el; // <- this is a DOM node, not a string ;-)
});
return data;
}
Slots as hyperHTML interpolations
Now that we have a way to address slots, all we need is a way to place these inside our layout.
Theoretically, we don't need Custom Elements to make it possible.
document.querySelectorAll('user-data').forEach(el => {
// retrieve slots as data
const data = slotsAsData(el);
// place data within a more complex template
hyperHTML.bind(el)`
<div class="user">
<div class="avatar">
${data.avatar}
</div>
${data.nickName}
${data.fullName}
</div>`;
});
However, if we'd like to use Shadow DOM to keep styles and node safe from undesired page / 3rd parts pollution, we can do it as shown in this Code Pen example based on Custom Elements.
As you can see, the only needed API is the attachShadow one and there is a super lightweight polyfill for just that that weights 1.6K min-zipped.
Last, but not least, you could use slots inside hyperHTML template literals and let the browser do the transformation, but that would need heavier polyfills and I would not recommend it in production, specially when there are better and lighter alternatives as shown in here.
I hope this answer helped you.
I have a similar approach, i created a base element (from HyperElement) that check the children elements inside a custom element in the constructor, if the element doesn't have a slot attribute im just sending them to default slot
import hyperHTML from 'hyperhtml/esm';
class HbsBase extends HyperElement {
constructor(self) {
self = super(self);
self._checkSlots();
}
_checkSlots() {
const slots = this.children;
this.slots = {
default: []
};
if (slots.length > 0) {
[...slots].map((slot) => {
const to = slot.getAttribute ? slot.getAttribute('slot') : null;
if (!to) {
this.slots.default.push(slot);
} else {
this.slots[to] = slot;
}
})
}
}
}
custom element, im using a custom rollup plugin to load the templates
import template from './customElement.hyper.html';
class CustomElement extends HbsBase {
render() {
template(this.html, this, hyperHTML);
}
}
Then on the template customElement.hyper.html
<div>
${model.slots.body}
</div>
Using the element
<custom-element>
<div slot="body">
<div class="row">
<div class="col-sm-6">
<label for="" class="">Name</label>
<p>
${model.firstName} ${model.middleInitial} ${model.lastName}
</p>
</div>
</div>
...
</div>
</custom-element>
Slots without shadow dom are supported by multiple utilities and frameworks.
Stencil enables using without shadow DOM enabled. slotted-element gives support without framework.

Search on descendants of an element

With protractor whats the best way to select child elements? Say we have the layout below...
<div id='parent_1'>
<div class='red'>Red</div>
<div class='blue'>Blue</div>
</div>
<div id='parent_2'>
<div class='red'>Red</div>
<div class='blue'>Blue</div>
</div>
With jQuery we'd do something like this.
var p1 = $('#parent_1');
var p1_red = $('.red', p1); //or p1.find('.red');
var p1_blue = $('.blue', p1); //or p1.find('.blue');
But with Protractor does it make sense to first get the parent element?
Since doing this var p1 = element('#parent_1'); doesn't actually retrieve/search for the object until getText() or something is called.
so doing this..
Scenario 1
expect(p1.element('.red')).toBe('red');
expect(p1.element('.blue')).toBe('blue');
OR
Scenario 2
expect(element('#parent_1').element('.red')).toBe('red');
expect(element('#parent_1').element('.blue')).toBe('blue');
OR
Scenario 3
expect(element('#parent_1 > .red')).toBe('red');
expect(element('#parent_1 > .blue')).toBe('blue');
Are there any benefits in one approach over the other?
This is what I'm doing but I don't know if there's any advantage of separating the parent from the cssSelector:
function getChild(cssSelector, parentElement){
return parentElement.$(cssSelector);
}
var parent = $('#parent_1');
var child_red = getChild('.red', parent);
var child_blue = getChild('.blue', parent);
Looking at Protractor's elementFinder I could be doing this:
function getChild(cssSelector, parentCss){
return $(parentCss).$(cssSelector);
}
var child_red = getChild('.red', '#parent_1');
var child_blue = getChild('.blue', '#parent_1');
The advantage of separating the child from the child css selector would only be if you'd like to use the parent for something else. Otherwise, it's slightly faster to do it in one call, like expect(element('#parent_1 > .red')).toBe('red'); since Protractor doesn't need to make two calls to the browser in this case.
Another reason to use the first approach would be if you were using a Locator strategy that cannot be expressed in CSS. For example:
var parent = element(by.css('.foo'));
var child = parent.element(by.binding('childBinding'));
expect(child.getText()).toEqual('whatever');

Class prefix as selector for each function

I am able to do this using an ID prefix as the selector, but I need to be able to do it with classes instead. It's an each function for opening up different modal windows on the same page. I need to avoid using ID names because I have some modal windows that will have multiple links on the same page, and when using IDs, only the first link will work.
So here's the function as it works with IDs:
$('div[id^=ssfamodal-help-]').each(function() {
var sfx = this.id,
mdl = $(this),
lnk = $('.link-' + sfx),
cls = $('.ssfamodal-close'),
con = $('.ssfamodal-content');
lnk.click(function(){
mdl.show();
});
cls.click(function(){
mdl.hide();
});
mdl.click(function() {
mdl.hide();
});
con.click(function() {
return false;
});
});
and I'm trying to change it to classes instead, like:
$('div[class^=ssfamodal-help-]').each(function() {
var sfx = this.attr('class'),
etc.
But I cannot get it to work without using IDs. Is it possible?
EDIT Fixed error with semi-colon at end of Vars, and updated Fiddle with the fix. Still not working though.
Here's a Fiddle
** UPDATE **
To be clearer, I need to be able to refer to the same modal more than once on the same page. E.g.:
MODAL 1
MODAL 2
MODAL 3
MODAL 4
LINK TO MODAL 1
LINK TO MODAL 2
LINK TO MODAL 3
LINK TO MODAL 4
OTHER STUFF
LINK TO MODAL 1
LINK TO MODAL 4
LINK TO MODAL 3
OTHER STUFF
LINK TO MODAL 2
ETC.
When using classes get rid of the ID habit :
className1, className2, className3 ... etc
simply use
className
HTML:
<div class="ssfamodal-help-base ssfamodal-backdrop">
<div id="help-content" class="ssfamodal-content">
<span class="ssfamodal-close">[x]</span>
Howdy
</div>
</div>
<div class="ssfamodal-help-base ssfamodal-backdrop">
<div id="help-content" class="ssfamodal-content">
<span class="ssfamodal-close">[x]</span>
Howdy Ho
</div>
</div>
<span class="link-ssfamodal-help-base">One</span>
<span class="link-ssfamodal-help-base">Two</span>
LIVE DEMO
var $btn = $('.link-ssfamodal-help-base'),
$mod = $('.ssfamodal-help-base'),
$X = $('.ssfamodal-close');
$btn.click(function(i) {
var i = $('[class^="link-"]').index(this); // all .link-** but get the index of this!
// Why that?! cause if you only do:
// var i = $('.link-ssfamodal-help-base').index();
// you'll get // 2
// cause that element, inside a parent is the 3rd element
// but retargeting it's index using $('className').index(this);
// you'll get the correct index for that class name!
$('.ssfamodal-help-base').eq(i).show() // Show the referenced element by .eq()
.siblings('.ssfamodal-help-base').hide(); // hide all other elements (with same class)
});
$X.click(function(){
$(this).closest('.ssfamodal-help-base').hide();
});
From the DOCS:
http://api.jquery.com/eq/
http://api.jquery.com/index/
http://api.jquery.com/closest/
Here I created a quite basic example on how you can create a jQuery plugin of your own to handle modals: http://jsbin.com/ulUPIje/1/edit
feel free to use and abuse.
The problem is that class attributes can consist of many classes, rather than IDs which only have one value. One solution, which isn't exactly clean, but seems to work is the following.
$('div').filter(function () {
var classes = $(this).attr('class').split(/\s+/);
for (var i = 0; i < classes.length; i++)
if (classes[i].indexOf('ssfamodal-help-') == 0)
return true;
return false;
}).each(function() {
// code
});
jsFiddle
Or, equivalently
$('div').filter(function () {
return $(this).attr('class').split(/\s+/).some(function (e) {
return e.indexOf('ssfamodal-help-') == 0;
});
}).each(function() {
// code
});
jsFiddle
If there is one-to-one relationship between the modal helps and the modal links which it appears there is...can simplfy needing to match class values by using indexing.
For this reason you don't need unique class names, rather they just overcomplicate things. Following assumes classes stay unique however
var $helps=$('div[id^=ssfamodal-help-]');
var $help_links=$('div[id^=link-ssfamodal-help-]');
$help_links.click(function(){
var linkIndex= $help_links.index(this);
$helps.hide().eq( linkIndex ).show();
});
/* not sure if this is what's wanted, but appeared original code had it*/
$helps.click(function(){
$(this).hide()
})
/* close buttons using traverse*/
$('.ssfamodal-close').click(function(){
$(this).closest('div[id^=ssfamodal-help-]' ).hide();
});
Also believe that this code is a little more readable than original apporach
DEMO
Can you try this,
$('div[class^=ssfamodal-help-]').each(function() {
var sfx = $(this).attr('class');
console.log(sfx);
/*console log:
ssfamodal-help-base ssfamodal-backdrop
ssfamodal-help-base2 ssfamodal-backdrop
*/
});
Demo: http://jsfiddle.net/xAssR/51/
why don't you write like
$('div.classname').each(function() {
// you can write your desired code here
var sfx = this.attr('class');
var aa= this.attr('id');
});
or
$('.classname').each(function() {
// you can write your desired code here
var sfx = this.attr('class');
var aa= this.attr('id');
});
where classname is the name of the class used for the div in html
Thanks.

Unbind view model from view in knockout

I'm looking for unbind functionality in knockout. Unfortunately googling and looking through questions asked here didn't give me any useful information on the topic.
I will provide an example to illustrate what kind of functionality is required.
Lets say i have a form with several inputs.
Also i have a view model binded to this form.
For some reason as a reaction on user action i need to unbind my view model from the form, i.e. since the action is done i want all my observables to stop reacting on changes of corresponding values and vise versa - any changes done to observables shouldn't affect values of inputs.
What is the best way to achieve this?
You can use ko.cleanNode to remove the bindings. You can apply this to specific DOM elements or higher level DOM containers (eg. the entire form).
See http://jsfiddle.net/KRyXR/157/ for an example.
#Mark Robinson answer is correct.
Nevertheless, using Mark answer I did the following, which you may find useful.
// get the DOM element
var element = $('div.searchRestults')[0];
//call clean node, kind of unbind
ko.cleanNode(element);
//apply the binding again
ko.applyBindings(searchResultViewModel, element);
<html>
<head>
<script type="text/javascript" src="jquery-1.11.3.js"></script>
<script type="text/javascript" src="knockout-2.2.1.js"></script>
<script type="text/javascript" src="knockout-2.2.1.debug.js"></script>
<script type="text/javascript" src="clickHandler.js"></script>
</head>
<body>
<div class="modelBody">
<div class = 'modelData'>
<span class="nameField" data-bind="text: name"></span>
<span class="idField" data-bind="text: id"></span>
<span class="lengthField" data-bind="text: length"></span>
</div>
<button type='button' class="modelData1" data-bind="click:showModelData.bind($data, 'model1')">show Model Data1</button>
<button type='button' class="modelData2" data-bind="click:showModelData.bind($data, 'model2')">show Model Data2</button>
<button type='button' class="modelData3" data-bind="click:showModelData.bind($data, 'model3')">show Model Data3</button>
</div>
</body>
</html>
#Mark Robinson gave perfect solution, I've similar problem with single dom element and updating different view models on this single dom element.
Each view model has a click event, when click happened everytime click method of each view model is getting called which resulted in unnecessary code blocks execution during click event.
I followed #Mark Robinson approach to clean the Node before apply my actual bindings, it really worked well.
Thanks Robin.
My sample code goes like this.
function viewModel(name, id, length){
var self = this;
self.name = name;
self.id = id;
self.length = length;
}
viewModel.prototype = {
showModelData: function(data){
console.log('selected model is ' + data);
if(data=='model1'){
ko.cleanNode(button1[0]);
ko.applyBindings(viewModel1, button1[0]);
console.log(viewModel1);
}
else if(data=='model2'){
ko.cleanNode(button1[0]);
ko.applyBindings(viewModel3, button1[0]);
console.log(viewModel2);
}
else if(data=='model3'){
ko.cleanNode(button1[0]);
ko.applyBindings(viewModel3, button1[0]);
console.log(viewModel3);
}
}
}
$(document).ready(function(){
button1 = $(".modelBody");
viewModel1 = new viewModel('TextField', '111', 32);
viewModel2 = new viewModel('FloatField', '222', 64);
viewModel3 = new viewModel('LongIntField', '333', 108);
ko.applyBindings(viewModel1, button1[0]);
});