I am trying to get an array of DOM-Elements in Vue.js. If I had the following HTML structure:
<select onchange="toggleDisability(this);" class="mySelect" id="mySelect1">
</select>
<select onchange="toggleDisability(this);" class="mySelect" id="mySelect2">
</select>
I could get all elements with the mySelect class with normal JS like:
var arraySelects = document.getElementsByClassName('mySelect');
Now I am trying to get the same thing with Vue $refs, but I am always getting the last element. it looks like:
<select id="selection-x" ref="Axis" #change="log($event)"></select>
<select id="selection-y" ref="Axis" #change="log($event)"></select>
and
log(selectElement){
var arraySelects = this.$refs['Axis'];
}
Of course there are also options ,so that #change event gets emitted, but it doesn't do what I want it to. I want to get an array of the elements with the same ref just like it works in the example above for normal JS, where you are getting an array of select elements whose class attribute equals to mySelect.
P.S. I know ref should be unique, but how could it be then used for this particular usecase?
No. It is not possible with ref and $refs. If you wish to do DOM manipulation then, use vue-directive or directly access DOM from the root element of the component like:
Vue.extend({
mounted() {
// Do what you want with your children.
const children = this.$el.querySelectorAll('.mySelect');
}
})
For me the best way to do this was to set a ref on the parent element (thanks joaner in original comment), but then I also needed to run my code in the "updated" hook so my data was loaded before trying to access the dom (I also have a v-if on the same element I want to reference children):
template:
<ul v-if="dataLoaded" ref="eventlist">
<li class="eventItem"></li>
<li class="eventItem"></li>
<li class="eventItem"></li>
</ul>
javascript:
updated() {
let eventItems = this.$refs.eventlist.children
console.log(eventItems)
}
Related
What is the easiest way to retrieve the (id of the) node that was just clicked on in a <v-treeview>? There is the update:open event that emits an array of all open nodes. I could temporarily store the whole array and compare a new version with the old one to see which element was added or removed. But this seems to be a bit cumbersome, right? I want to use the id of the node to dynamically reload the children of the children of the node from the backend, so that the user has the feeling that the data is already in the frontend. Maybe it is possible to emit in the update:open event not the whole array, but only the current node, somehow like this: #update:open="onExpand(item)"? (This throws the error Property or method "item" is not defined.)
You could use VTreeView's label slot, which passes the item itself in its slot props. In that slot, you could render a span with a click handler that also includes the item:
<template>
<v-treeview :items="items">
<template #label="{ item }">
<span #click="onItemClick(item)">{{item.name}}</span>
</template>
</v-treeview>
</template>
<script>
export default {
methods: {
onItemClick(item) {
console.log(item.id)
}
}
}
</script>
You can use update:active with the return-object prop. It will return the full object instead of the node id.
https://vuetifyjs.com/en/api/v-treeview/#props
<ul>
{
this.state.pics ? this.state.pics.map((pic, index) => {
return (
<li key={index}>
<img src={pic.url} height="200" alt="Image preview..." />
<select data-id={index} onChange={this.setValue}>
<option value="1" >Monday</option>
<option value="2">Tuesday</option>
<option value="3">Wednesday</option>
<option value="4">Thursday</option>
<option value="4">Friday</option>
<option value="4">Saturday</option>
<option value="4">Sunday</option>
</select>
</li>
)
}) : ""
}
</ul>
On every change, I would like to get the value of data-id attribute which is nothing but value of index . or is there a way to directly access the value of key attribute of li . I am using react . Also is it a good practice in react to directly access the dom by query selector
const setValue = (e) => {
console.log(e.target.getAttribute('data-id'))
}
The onChange callback gets the normal javascript event object which can be used to access the attribute you want.
Apart from pure JS based solutions of accessing the DOM via event APIs (in your case), you can also use the Refs feature of React
But as the docs themselves say
Avoid using refs for anything that can be done declaratively.
In your case, accessing the select element's custom attributes are pretty straightforward
Create a ref to the element you wish to access the DOM
Assign it to a variable which is accessible in your event callback
Access the variable ,which has the DOM reference in your function.
<select
ref={select => (this.selectElement = select)}
data-id={1}
onChange={this.setValue}>
A sample can be found here in the CodeSandox link
And to answer your other question "Also is it a good practice in react to directly access the dom by query selector" - I'd say use that as a last resort , most of the problems can be solved by moving around props and states in component heirarchy.
Recommended reading
Lifting State Up
Thinking in React
You could try something like this $(this).parent().attr('data-id');
I got 1 select attribute
I got something to trigger for the select
first:
<select id="select-room" data-room="inhouse"></select>
second:
<select id="select-room" data-room="ar_account"></select>
I have already done this the thing is when I make a function for this thing it wont work like:
$('#select-room').on('change','[data-room="inhouse"]', this.Change1);
$('#select-room').on('change','[data-room="ar_account"]', this.Change2);
the thing is these 2 functions wont work
There should not be same id in one DOM. Because when you do so, the DOM will search for the id (here "select-room") from the top and it will consider the first one comes across.
So this is a bad idea to use the same id multiple times. Use class instead.
<select class="select-room" data-room="inhouse"></select>
<select class="select-room" data-room="ar_account"></select>
$('.select-room[data-room="inhouse"]').on('change', this.Change1);
$('.select-room[data-room="ar_account"]').on('change', this.Change2);
Firstly, you should not use same id for multiple elements, selector will only target first matched element, I have changed it to class in the code. If the issue is that data-room is changing for element, you can use event delegation here and attach event to document instead or to some parent element, like this:
<select class="select-room" data-room="inhouse"></select>
<select class="select-room" data-room="ar_account"></select>
$(document).on('change', '.select-room[data-room="inhouse"]', this.Change1);
$(document).on('change', '.select-room[data-room="ar_account"]', this.Change2);
I have a component and its inner html is dynamically changing. I do some operation on the child DOM elements of this component, so I have to repeat the operations each time the inner html of this component is changed.
For example in the component:
myElements = this.el.nativeElement.querySelectorAll('.myClass');
// do something with that DOM elements
And in the view:
<div *ngFor="let item of myItems">
<div class="myClass">...</div>
</div>
I have to call
myElements = this.el.nativeElement.querySelectorAll('.myClass');
Each time myItems is updated and new DOM elements loaded.
Can I bind the innerHTML to ngOnChange() or is there other ways to achieve this?
You could try ngDoCheck:
https://angular.io/docs/ts/latest/api/core/index/DoCheck-class.html
You would manually check the old value and the new value of the innerHtml, then execute your custom code.
Potential example:
public ngDoCheck(){
if(this.oldInnerHtml != this.newInnerHtml){
//take custom action
}
}
On my page there are a few <select> html elements, each of them with multiple <option> values. I would like the same <select> elements to be used in a MapBox description toolkit, which expects me to pass a HTML source into its setHTML method.
The simple way I do it now is:
var tableOption = document.getElementById(selectId);
var html = tableOption.innerHTML;
return "<select>" + html + "</select>";
This works, however it has a drawback of passing the original html source, therefore it does not reflect which option is selected right now. I know I can get currently selected option with value or selectedIndex, and it should be possible to parse the HTML I have obtained and remove and add selected property to a corresponding node in JS, however this seems a bit complicated.
Is there some easier way how to get a HTML source which could be used to construct a copy of a select element with the exact selection state it has now?
I would prefer a solution without jQuery if possible.
You could just wrap your select in another element. Then you'd be able to select it by id, get the parent node, then the parent node's innerHTML. If you want to show which option is currently selected, you can place the selected attribute on it.
document.getElementById('selectId').addEventListener('change', function() {
var opts = this.getElementsByTagName('option');
for (var i = 0; i < opts.length; i++)
opts[i].removeAttribute('selected');
this.querySelector('option:checked').setAttribute('selected', 'selected');
console.log(this.parentNode.innerHTML);
});
<div class="select-wrapper">
<select id="selectId">
<option>Yes</option>
<option>No</option>
</select>
</div>
Just clone the element and then set the selectedIndex - things like event handlers probably wouldn't be copied though
I realize you asked for a simpler solution but I can't think of one much simpler considering it's only 4 lines
Fiddle https://jsfiddle.net/s8mb0mxp/2/
HTML
<select id="original">
<option>1</option>
<option>2</option>
<option>3</option>
</select>
<button>
Clone
</button>
<div id="target">
</div>
JS
$('button').on('click', function(){
var $original = $('#original');
var el = $original.clone(); // Clone the object
$('#target').append(el) // Attach the new object to an element
el.find('option')[$original[0].selectedIndex].setAttribute('selected', 'selected')
console.log(el[0].outerHTML)
});