The container element only determines the coordinate system of the event.x and event.y coordinates. That would be useful if, for instance, your draggable circle were being positioned relative to another element using the "dx" and "dy" attributes supported by some SVG elements. However, container() will not serve to automatically constrain event.x and event.y to fit within a given element's visual bounds.
To elaborate upon Ouroborus's comment: You have the following lines in your drag event handler:
circle.attr("cx", d => event.x).attr("cy", d => event.y))
In the functions "d => event.x" and "d => event.y" you must constrain event.x and event.y according to a mathematical formula corresponding to what you have referred to as your "container's" shape.
For example, to constrain the draggable element to remain within a rectangular region, the formula is simple:
circle.attr('cx', d => {
if (event.x > 60 + 240) { // El dragged beyond the right-hand border
return 60 + 240
} else if (event.x < 60) { // El dragged beyond the left-hand border
return 60
} else { // El is within the box's horizontal bounds
return event.x
}
})
For the y-axis, the bounding formula follows analogously.
The end result looks like this (run the snippet and try it out):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://d3js.org/d3.v6.min.js"></script>
<style>
</style>
</head>
<body>
<script>
const svg = d3.select('body').append('svg').attr('width', '600').attr('height', '500').style('background', 'lightblue')
const containingRect = svg.append('rect')
.attr('x', '60')
.attr('y', '80')
.attr('width', '240')
.attr('height', '240')
.attr('fill', 'grey')
// Can I use this circle as the container?
const circleAsContainer = svg.append('circle').attr('cx', '180').attr('cy', '200').attr('r', '120').attr('fill', 'white')
const circle = svg.append('circle').attr('cx', '120').attr('cy', '150').attr('r', '30').attr('fill', 'orange')
.call(d3.drag()
.on('start', (event, d) => circle.attr('stroke', 'black').attr('stroke-width', '4'))
.on('drag', (event, d) =>
circle.attr('cx', d => {
if (event.x > 60 + 240) { // El dragged beyond the right-hand border
return 60 + 240
} else if (event.x < 60) { // El dragged beyond the left-hand border
return 60
} else { // El is within the box's horizontal bounds
return event.x
}
}).attr('cy', d => {
if (event.y > 80 + 240) { // El dragged beyond the top border
return 80 + 240
} else if (event.y < 80) { // El dragged beyond the bottom border
return 80
} else { // El is within the box's vertical bounds
return event.y
}
})
).on('end', (event, d) => circle.attr('stroke', 'none'))
)
</script>
</body>
</html>
To constrain the element to be dragged within a circular region, you must solve a different math problem. If I understand your question correctly, you would like the element to end up at the coordinates (targetX, targetY) in the event that the draggable element is dragged to (x, y) outside of the containing circular region: 
In this diagram, (x, y) represent event.x and event.y. (circleCenterX, circleCenterY) are the coordinates cx, cy of the bounding circle, and radius represents the radius of the bounding circle. Given these five values, I think that you would like to calculate targetX and targetY.
Luckily for you, I have solved this problem myself for a project of my own! Here is a function to calculate targetX and targetY:
/**
* @author Ann Yanich (ann.yanich@posteo.de)
* Based loosely on TWiStErRob's answer https://stackoverflow.com/a/31254199/7359454
* @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/
* Finds the intersection point between:
* * The circle of radius at (circleCenterX, circleCenterY)
* and
* * The half line going from (x, y) to the center of the circle
*
* I know this works from outside the circle, but I have not taken the
* time to reason out what would happen if (x, y) lie inside the circle.
*
* @param x:Number x coordinate of point to build the half-line from
* @param y:Number y coordinate of point to build the half-line from
* @param radius:Number the radius of the circle
* @param circleCenterX:Number The X coordinate of the center of the circle
* @param circleCenterY:Number The Y coordinate of the center of the circle
* @param validate:boolean (optional) whether to treat point inside the circle as error
* @return an object with x and y members for the intersection
* @throws if validate == true and (x,y) is inside the circle
*/
function pointOnCircle (x, y, radius, circleCenterX, circleCenterY, validate) {
const dx = circleCenterX - x
const dy = circleCenterY - y
const distance = Math.sqrt(dx * dx + dy * dy)
if (validate && distance < radius) {
throw new Error('Point ' + [x, y] + 'cannot be inside the circle centered at ' +
[circleCenterX, circleCenterY] + ' with radius' + radius + '.')
}
const normX = dx / distance
const normY = dy / distance
const targetX = circleCenterX - radius * normX
const targetY = circleCenterY - radius * normY
return {
x: targetX,
y: targetY
}
}
You would use this function as follows:
circle.attr("cx", d => {
if (isInCircle(event.x, event.y)) {
return event.x;
} else {
return pointOnCircle(event.x, event.y, radius, circleCenterX, circleCenterY, true)["x"];
}
}).attr("cy", d => {...})
I will leave the implementation of "isInCircle()" up to you. I hope that this is enough information for you to be able to piece together a working solution :)
All the best,
Ann