Yes, you'd use JavaScript, and yes, you'll have to deal with the managed ID values somehow. One reasonable trick is to not rely on the ID, but instead something else you can query.
<apex:selectList html-data-selList="1" onchange="updatehidden()" ...
<apex:inputField html-data-inpField="1" onchange="updatehidden()" ...
<apex:inputHidden html-data-inpHidden="1" ...
Your function can then query for the custom attributes:
function updatehidden() {
var selList = document.querySelector("[data-selList]"),
inpField = document.querySelector("[data-inpField]"),
inpHidden = document.querySelector("[data-inpHidden]");
...
}
Of course, this isn't the only way; many people use custom attributes or custom style classes, which you can also query on. It's also possible to query based on a partial ID, too:
<apex:selectList id="sellist" onchange="updatehidden()" ...
<apex:inputField id="inpfield" onchange="updatehidden()" ...
<apex:inputHidden id="inphidden" ...
var sellist = document.querySelector("[id*=sellist]");
var inpfield = document.querySelector("[id*=inpfield]");
var inphidden = document.querySelector("[id*=inphidden]");
Self-Contained Example:
<apex:page standardController="Account">
<script>
function updatehidden() {
var selList = document.querySelector("[data-selList]").value,
inpField = document.querySelector("[data-inpField]").value,
inpHidden = document.querySelector("[data-inpHidden]");
inpHidden.value = selList + ' - ' + inpField;
}
</script>
<apex:form>
<apex:selectList html-data-selList="1" size="1" id="sellist" onchange="updatehidden()">
<apex:selectOption itemLabel="value 1" itemValue="value 1" />
<apex:selectOption itemLabel="value 2" itemValue="value 2" />
<apex:selectOption itemLabel="value 3" itemValue="value 3" />
</apex:selectList>
<apex:inputField html-data-inpField="1" id="inpfield" onchange="updatehidden()" value="{!Account.Name}" />
<apex:inputHidden html-data-inpHidden="1" />
</apex:form>
</apex:page>